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

proposal: Go 2: hexadecimal floats #29008

Open
seebs opened this Issue Nov 29, 2018 · 7 comments

Comments

Projects
None yet
6 participants
@seebs
Copy link
Contributor

seebs commented Nov 29, 2018

What version of Go are you using (go version)?

1.11, but N/A

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

N/A

What did you do?

Read the fmt specs and the language spec, and ponder floating point representation.

What did you expect to see?

Support for hexadecimal floats.

What did you see instead?

No support for hexadecimal floats.

I'm aware that Go's decimal-parsing for floats is superior to that available to C compilers in a number of ways (see issue #12518), but people who want hexadecimal floats often prefer them for reasons beyond that. This does have some overlap with 12518's suggestion for allowing binary exponents with a p suffix, but hexadecimal floats give stronger and easier-to-reason-about guarantees. With decimal representations, it is non-trivial for a human to tell at a glance whether the representation is precise. With hexadecimal floats, the representation is definitionally precise.

Ideally, this would be present in fmt (both for input and output), and also in numeric constants. C used %a/%A, and the format [-]0xh.hhhp[+-]d, with the hexadecimal digits after the period optional, and I don't see any obvious reason to think this specification would cause problems. So far as I know, this wouldn't break any existing code (any valid hex literal would be a syntax error in current go), and it would make life marginally easier for some people doing numerical work.

@gopherbot gopherbot added this to the Proposal milestone Nov 29, 2018

@gopherbot gopherbot added the Proposal label Nov 29, 2018

@ianlancetaylor ianlancetaylor changed the title proposal: hexadecimal floats proposal: Go 2: hexadecimal floats Nov 29, 2018

@seebs

This comment has been minimized.

Copy link
Contributor

seebs commented Nov 29, 2018

Someone pointed out that I haven't explained why I don't think Float64FromBits solves this problem.

  1. It's runtime.
  2. It requires you to count a lot of zeroes to write a simple value.
0x1p-3
0.125
Float64FromBits(0x3fc0000000000000)

In this case, obviously decimal is fine, but I am pretty convinced that a hex float literal is way more legible than Float64FromBits for that value.

@josharian

This comment has been minimized.

Copy link
Contributor

josharian commented Dec 1, 2018

Two observations:

With appropriate compiler optimizations, it doesn’t have to be runtime.

If #28493 goes in, the zeros will be easier to count.

@seebs

This comment has been minimized.

Copy link
Contributor

seebs commented Dec 1, 2018

So that's a good counterargument to what I said, but I don't feel persuaded, making me think I didn't actually say anything that communicated what I'm looking for. A better way of expressing it might be: I don't want to have to think about the zeroes at all. They're a weird artifact of the normalized representation. Say I want to have a floating-point constant. if I write it as a hex float, I get a 100% predictable float constant that can be used for both float32 and float64, assuming they have sufficient precision, and I know exactly what it means in either of them, and don't have to guess about or think about rounding errors. If I write it in Float##FromBits, I have to have two forms of it because they're different lengths.

Hex floats offer a convenient way to express mantissa values with reliable precision. They are much more human-reader friendly than IEEE's normalized mantissa values, and getting the exponent in decimal, but still in powers of 2, turns out to map nicely onto what's actually happening.

When people first proposed this for C, I was... sort of skeptical. Why would you want floats in hex? That's not how they work! That's not how anything works! But there were enough floating point people on the committee to get it adopted, and I didn't really have an objection, I just didn't see the point. But as I've spent time trying to decipher float values, it's become increasingly clear to me that, while dealing with the whole float as a large unsigned integer certainly works, it's not really easy to read. I can't look at a Float##FromBits value and know what it is without having to do some kind of math in my head. Why is 3fc negative? Because the sign bit for the exponent is the 4, which isn't where I normally look for sign bits, because the 8 is the sign bit for the whole number. Why is a 13-byte string of 0s actually a 1? (Any short punchy explanation is probably wrong.)

It's a lot easier for me to understand "p-3" than "0x3FC". YMMV.

EDIT: I was wrong, though, the 4 isn't the sign bit, the exponent is stored unsigned using a bias of +127. This goes back to my basic assertion that this is not a programmer-friendly way to view float values.

@baryluk

This comment has been minimized.

Copy link

baryluk commented Dec 1, 2018

Some examples:

0x1.FFFFFFFFFFFFFp1023 // double.max
0x1p-52                // double.epsilon
1.175494351e-38F       // float.min
2.645_751
6.022140857E+23
6_022.140857E+20
6_022_.140_857E+20_

Obviously both could be combined.

0x1.F_FFFF_FFFF_FFFFp1_023
@rsc

This comment has been minimized.

Copy link
Contributor

rsc commented Jan 17, 2019

We've posted a combined proposal for #19308, #12711, #28493, and #29008 at golang.org/design/19308-number-literals. It includes hex floats as suggested in this issue. (I could have sworn there was an earlier issue proposing hex floats, but I can't find it.)

Note that this will be the first proposal to follow the process outlined in the Go 2 here we come! blog post: we will have everything ready to go and checked in at the start of the Go 1.13 cycle (Feb 1), we will spend the next three months using those features and soliciting feedback based on actual usage, and then at the start of the release freeze (May 1), we will make the "launch decision" about whether to include the work in Go 1.13 or not.

Thanks for your feedback and all your help improving Go.

@seebs

This comment has been minimized.

Copy link
Contributor

seebs commented Jan 17, 2019

Thanks! The proposal looks excellent to me.

@gopherbot

This comment has been minimized.

Copy link

gopherbot commented Jan 22, 2019

Change https://golang.org/cl/157677 mentions this issue: cmd/compile: accept new Go2 number literals

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment