Skip to content

[encoding.binary] signed integer functions #23828

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
2 tasks
einar-hjortdal opened this issue Feb 28, 2025 · 11 comments
Closed
2 tasks

[encoding.binary] signed integer functions #23828

einar-hjortdal opened this issue Feb 28, 2025 · 11 comments

Comments

@einar-hjortdal
Copy link
Contributor

einar-hjortdal commented Feb 28, 2025

Describe the feature

Currently, encoding.binary has functions to encode and decode unsigned integers exclusively. I recently needed to encode and decode signed integers and could not use the encoding.binary functions, as they exclusively handle unsigned integers.

Use Case

I needed this feature to implement a binary protocol client that uses integers in the message exchanged over the network.

Proposed Solution

No response

Other Information

No response

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

Version used

V 0.4.9 5f5e48e

Environment details (OS name and version, etc.)

|V full version      |V 0.4.9 537605a
|:-------------------|:-------------------
|OS                  |linux, "openmamba release 2024.6 for x86_64 (rolling)"
|Processor           |8 cpus, 64bit, little endian, Intel(R) Core(TM) i7-4790K CPU @ 4.00GHz
|Memory              |4.63GB/31.17GB
|                    |
|V executable        |/home/einar/.local/lib64/v/v
|V last modified time|2025-02-25 15:36:48
|                    |
|V home dir          |OK, value: /home/einar/.local/lib64/v
|VMODULES            |OK, value: /home/einar/.vmodules
|VTMP                |OK, value: /tmp/v_1000
|Current working dir |OK, value: /home/einar/Documents/projects/vlang/active/firebird
|                    |
|Git version         |git version 2.48.1
|V git status        |weekly.2025.09-2-g537605a0 (17 commit(s) behind V master)
|.git/config present |true
|                    |
|cc version          |cc (GCC) 14.2.1 20240909
|gcc version         |gcc (GCC) 14.2.1 20240909
|clang version       |N/A
|tcc version         |tcc version 0.9.28rc 2024-07-31 HEAD@1cee0908 (x86_64 Linux)
|tcc git status      |thirdparty-linux-amd64 0134e9b9
|emcc version        |N/A
|glibc version       |ldd (GNU libc) 2.41

Note

You can use the 👍 reaction to increase the issue's priority for developers.

Please note that only the 👍 reaction to the issue itself counts as a vote.
Other reactions and those to comments will not be taken into account.

Copy link

Connected to Huly®: V_0.6-22233

@jorgeluismireles
Copy link

I have faced this problem but a workaround is not too difficult, after endianess was resolved:

write_a := i32(-3)
write_b := u32(write_a)
println('${write_a:x} ${write_b:x}') // -3 fffffffd

read_a := u32(0xfffffffd)
read_b := i32(read_a)
println('${read_a:x} ${read_b:x}') // fffffffd -3

In another similar problem, I wanted to store a []u8 of maximum length of 8 in sqlite as a number (which saves space). But sqlite maximum number is i64 (signed 64 bits) not unsigned. So I solved the problem converting from signed to unsigned and worked for inserting and reading and works ok.

@JalonSolov
Copy link
Contributor

The cast from i32 to u32 should at least be giving a warning about possible loss of precision.

@jorgeluismireles
Copy link

Seems no precision is lost, just minus sign bit changing of meaning:

a := i32(-31415)
b := u32(a)
c := i32(b)
d := u32(c)
assert a == c
assert b == d

@einar-hjortdal
Copy link
Contributor Author

einar-hjortdal commented Feb 28, 2025

I agree with JalonSolov here, there is a chance of precision loss when moving between u32 and i32 because of the different ranges.
the u32 4_294_967_295 can't be cast to i32 without loss, and a negative i32 cannot be cast to u32 without loss. The compiler should warn when this dangerous operation is performed.
However if this is an implementation detail internal to encoding.binary it makes no difference to the user.

@jorgeluismireles
Copy link

Event though, as mentioned in issue #23782, signed integers are accepting already big positive numbers that are converted into the fly into negatives. Even though next example gives the same negative -56 I think there is a subtle difference:

s := i8(200) // I wonder this should be permitted
println('${s}') // -56
u := u8(200) // valid
v := i8(u) // I think this is Ok which helps to convert 
println('${v}') // -56

@einar-hjortdal
Copy link
Contributor Author

s := i8(200) // I wonder this should be permitted

I got confused by that too yesterday

@jorgeluismireles
Copy link

I agree with JalonSolov here, there is a chance of precision loss

Well I meant no single bit is destroyed into going signed and unsigned. Yes, precision or resolution is different, for i8 we can have exactly 127 positive numbers, one 0 and 128 negative numbers. For u8 we can have exactly 255 positive numbers and one 0.

@einar-hjortdal
Copy link
Contributor Author

Well I meant no single bit is destroyed into going signed and unsigned.

I think you're right.
I will be using your suggestion.
Though I believe new methods should be written for encoding.binary, even if they're just casting the result of the existing methods to a signed integer. Something like

pub fn big_endian_i32(b []u8) i32 {
	return i32(big_endian_u32(b))
}

@jorgeluismireles
Copy link

To complicate things there is no a single way to represent signed numbers: sign-magnitude, one's complement, two's complement and others. A rare protocol could be using other representation than V's cast.

@einar-hjortdal
Copy link
Contributor Author

Problem solved by #24106

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants