-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Finish and fix float printing #930
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
Conversation
Currently tested up to |
which build mode are you testing in? |
Debug mode only. Since there are outstanding issues for release modes I'll consider that in a separate PR once the base logic is known to work. |
Up to 1518338056 now. I think there is an issue with the actual errol reference code right now on some larger inputs. Will double check and confirm tomorrow. With the reference code:
We get:
In the zig port we actually seem to fill the buffer correctly (with the trailing 2) but the length of the slice is one short. I don't think I've overlooked anything obvious here. I'll try diagnose the issue and make a fix before reporting just yet. Also, here is an updated test script to ignore the wierd
Feels like floating point is one edge case after another. |
That's really frustrating. It might be worth giving up errol and having simpler, smaller code for parsing and printing floats. This is what I started in the Even if we had errol in the standard library for speed, we still might want this smaller code implementation, and the default implementation would choose based on --release-fast or --release-small. |
Just to clarify something: I'm aware of Zig's general stance on non-finite float values, which is that they usually shouldn't happen, but this formatting code still accepts non-finite values. Is there any kind of low level "fast math" formatting API that we want to have that does not accept non-finite values? Would we gain anything from such a function? JSON, for example, never needs to deal with non-finite floats. |
I'm not convinced that optimization is worth it. I think the 2 or 3 branches that check for non-finite float values are a negligible slice of the pie chart that is formatting float values. Also, even though the default float mode is Optimized, we still support Strict at a scope level. My stance is that formatting floats should work for all values. |
I think errol3 is still fine. This is the first edge case encountered after 1.5 billion floats so it's not too bad. I think we should strive for an accurate implementation and compared to other methods this strikes the best compromise as far as I can see. The code size is slightly large, but the only rationale for having a simpler float printing would be embedded in my view. 5-10Kb is worth the cost of a good implementation in all other cases. At least if someone isn't using the float formatting then it won't be compiled in. |
I've had a bit of a closer look into this and I'm fairly certain there is no bug in errol. This looks like a non-optimal rounding accuracy in the libc implementation. You can test Grisu3 as well, which is used by most major browser engines, by pasting |
I've tested the
What remains now is to do the following:
|
Few thoughts/questions:
|
I agree with this. My proposal would be
This makes sense to me. Perhaps we can find some inspiration from Python3's docs for formatting. |
This is mostly complete now. The windows test failure is a bit annoying. I'll see if I can test under wine or otherwise I'll set up a vm for it. Consider the formatting notation of scientific values temporary. I would like to review this the formatting string language completely and revamp as needed. I've tested scientific/decimal rounding to a precision of 5 for all f32 values against libc and they all match with a few minor exceptions which are noted in the tests.
|
- Fix errors printing very small numbers - Add explicit scientific output mode - Add rounding based on a specific precision for both decimal/exp modes. - Test and confirm exp/decimal against libc for all f32 values. Various changes to better match libc.
more details about the windows failure integer division by zero stack trace from msvc:
I'm quite puzzled. Happy to try anything you suggest. |
Some ideas I thought of to try next time I get a chance to look into this:
|
I did not solve it but I have a bunch of clues. One I believe is a red herring, but it is rather interesting - in this test case, we are invoking udivmodti4 with
The correct answer for division is However I think this is a red herring because the answer is consistent on linux and windows. Also note that on windows udivmodti4 passes the quite comprehensive test suite, but this uses normal zig function calls and does not try to use u128 types across .o file boundaries. Another clue is JuliaLang/julia#3081 This seems to point to a bug in LLVM since LLVM is the one generating calls to udivmodti4 in the first place, and we build compiler_rt.o with LLVM using the C calling convention. I will put together a small test case and send another email to llvm-dev. |
Here's a small test that fails on windows but passes on linux: const std = @import("std");
test "aoeu" {
var a: u128 = 152313999999999991610955792383;
var b: u128 = 10000000000000000000;
var c = a / b;
std.debug.assert(c == 15231399999);
} Later I'll work on turning this into LLVM IR and trying to trigger the problem using only clang and LLVM, which should get some attention on a bug report. |
Here's the latest mail I sent to llvm-dev: http://lists.llvm.org/pipermail/llvm-dev/2018-April/122775.html |
It seems that the problem here is that there is no ABI specification for how to handle i128 on Windows. It's not clear to me what calling convention LLVM is using to call export fn __udivti3(a: u128, b: u128) u128 {
@setRuntimeSafety(builtin.is_test);
return __udivmodti4(a, b, null);
} So here's how to solve this problem:
|
I believe the commit I just pushed should fix the compiler-rt ABI for windows. |
Thanks for tracking that down and fixing. |
Thank you for doing the floating point fixes! |
WIP: See #375.
A start at finishing off the float printing once and for all. I've pushed early since I want to confirm the changed behaviour.
For now, I've only done the
formatFloatDecimal
function. I've adjusted the code so we now round the last digit if required so we are in line with what glibc is doing. Was there any reason behind truncating or has we just not got around to implementing rounding? I've also fixed the negative exponential cases which were not handled previously. Infinity and NaN were renamed to match C.Here is a test program which I'd like to get passing before merging this. We'll need one for
formatFloat
, too, and should also randomly generate f64/double values and ensure coverage looks good on that area too.Currently we print the first 713620 values the same as libc. I'll work through the ones which don't and add test cases as new errors are encountered.