authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2018-04-23 17:18:05+12:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2018-04-23 17:22:51+12:00
loge5175d432ef01e078ef247ea0a781243219ddfb6
treee2f5131b9e7e9646888a84425b1d86c193ba904c
parentd8ba1bc12054712dec731db0c4062a5df0d627c6

Fix release float printing errors

Fixes #564. Fixes #669. Fixes #928.

2 files changed, 202 insertions(+), 201 deletions(-)

std/fmt/errol/index.zig+3
...@@ -259,6 +259,9 @@ fn gethi(in: f64) f64 {...@@ -259,6 +259,9 @@ fn gethi(in: f64) f64 {
259/// Normalize the number by factoring in the error.259/// Normalize the number by factoring in the error.
260/// @hp: The float pair.260/// @hp: The float pair.
261fn hpNormalize(hp: &HP) void {261fn hpNormalize(hp: &HP) void {
262 // Required to avoid segfaults causing buffer overrun during errol3 digit output termination.
263 @setFloatMode(this, @import("builtin").FloatMode.Strict);
264
262 const val = hp.val;265 const val = hp.val;
263266
264 hp.val += hp.off;267 hp.val += hp.off;
std/fmt/index.zig+199-201
...@@ -779,212 +779,210 @@ test "fmt.format" {...@@ -779,212 +779,210 @@ test "fmt.format" {
779 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);779 const result = try bufPrint(buf1[0..], "pointer: {}\n", &value);
780 assert(mem.startsWith(u8, result, "pointer: Struct@"));780 assert(mem.startsWith(u8, result, "pointer: Struct@"));
781 }781 }
782782 {
783 // TODO get these tests passing in release modes783 var buf1: [32]u8 = undefined;
784 // https://github.com/zig-lang/zig/issues/564784 const value: f32 = 1.34;
785 if (builtin.mode == builtin.Mode.Debug) {785 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
786 {786 assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));
787 var buf1: [32]u8 = undefined;787 }
788 const value: f32 = 1.34;788 {
789 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);789 var buf1: [32]u8 = undefined;
790 assert(mem.eql(u8, result, "f32: 1.34000003e+00\n"));790 const value: f32 = 12.34;
791 }791 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);
792 {792 assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));
793 var buf1: [32]u8 = undefined;793 }
794 const value: f32 = 12.34;794 {
795 const result = try bufPrint(buf1[0..], "f32: {e}\n", value);795 var buf1: [32]u8 = undefined;
796 assert(mem.eql(u8, result, "f32: 1.23400001e+01\n"));796 const value: f64 = -12.34e10;
797 }797 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
798 {798 assert(mem.eql(u8, result, "f64: -1.234e+11\n"));
799 var buf1: [32]u8 = undefined;799 }
800 const value: f64 = -12.34e10;800 {
801 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);801 // This fails on release due to a minor rounding difference.
802 assert(mem.eql(u8, result, "f64: -1.234e+11\n"));802 // --release-fast outputs 9.999960000000001e-40 vs. the expected.
803 }803 if (builtin.mode == builtin.Mode.Debug) {
804 {
805 var buf1: [32]u8 = undefined;804 var buf1: [32]u8 = undefined;
806 const value: f64 = 9.999960e-40;805 const value: f64 = 9.999960e-40;
807 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);806 const result = try bufPrint(buf1[0..], "f64: {e}\n", value);
808 assert(mem.eql(u8, result, "f64: 9.99996e-40\n"));807 assert(mem.eql(u8, result, "f64: 9.99996e-40\n"));
809 }808 }
810 {809 }
811 var buf1: [32]u8 = undefined;810 {
812 const value: f64 = 1.409706e-42;811 var buf1: [32]u8 = undefined;
813 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);812 const value: f64 = 1.409706e-42;
814 assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));813 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
815 }814 assert(mem.eql(u8, result, "f64: 1.40971e-42\n"));
816 {815 }
817 var buf1: [32]u8 = undefined;816 {
818 const value: f64 = @bitCast(f32, u32(814313563));817 var buf1: [32]u8 = undefined;
819 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);818 const value: f64 = @bitCast(f32, u32(814313563));
820 assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));819 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
821 }820 assert(mem.eql(u8, result, "f64: 1.00000e-09\n"));
822 {821 }
823 var buf1: [32]u8 = undefined;822 {
824 const value: f64 = @bitCast(f32, u32(1006632960));823 var buf1: [32]u8 = undefined;
825 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);824 const value: f64 = @bitCast(f32, u32(1006632960));
826 assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));825 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
827 }826 assert(mem.eql(u8, result, "f64: 7.81250e-03\n"));
828 {827 }
829 // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.828 {
830 // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.829 // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05.
831 var buf1: [32]u8 = undefined;830 // In fact, libc doesn't round a lot of 5 cases up when one past the precision point.
832 const value: f64 = @bitCast(f32, u32(1203982400));831 var buf1: [32]u8 = undefined;
833 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);832 const value: f64 = @bitCast(f32, u32(1203982400));
834 assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));833 const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
835 }834 assert(mem.eql(u8, result, "f64: 1.00001e+05\n"));
836 {835 }
837 var buf1: [32]u8 = undefined;836 {
838 const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);837 var buf1: [32]u8 = undefined;
839 assert(mem.eql(u8, result, "f64: nan\n"));838 const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
840 }839 assert(mem.eql(u8, result, "f64: nan\n"));
841 {840 }
842 var buf1: [32]u8 = undefined;841 {
843 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);842 var buf1: [32]u8 = undefined;
844 assert(mem.eql(u8, result, "f64: -nan\n"));843 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64);
845 }844 assert(mem.eql(u8, result, "f64: -nan\n"));
846 {845 }
847 var buf1: [32]u8 = undefined;846 {
848 const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);847 var buf1: [32]u8 = undefined;
849 assert(mem.eql(u8, result, "f64: inf\n"));848 const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
850 }849 assert(mem.eql(u8, result, "f64: inf\n"));
851 {850 }
852 var buf1: [32]u8 = undefined;851 {
853 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);852 var buf1: [32]u8 = undefined;
854 assert(mem.eql(u8, result, "f64: -inf\n"));853 const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
855 }854 assert(mem.eql(u8, result, "f64: -inf\n"));
856 {855 }
857 var buf1: [64]u8 = undefined;856 {
858 const value: f64 = 1.52314e+29;857 var buf1: [64]u8 = undefined;
859 const result = try bufPrint(buf1[0..], "f64: {.}\n", value);858 const value: f64 = 1.52314e+29;
860 assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));859 const result = try bufPrint(buf1[0..], "f64: {.}\n", value);
861 }860 assert(mem.eql(u8, result, "f64: 152314000000000000000000000000\n"));
862 {861 }
863 var buf1: [32]u8 = undefined;862 {
864 const value: f32 = 1.1234;863 var buf1: [32]u8 = undefined;
865 const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);864 const value: f32 = 1.1234;
866 assert(mem.eql(u8, result, "f32: 1.1\n"));865 const result = try bufPrint(buf1[0..], "f32: {.1}\n", value);
867 }866 assert(mem.eql(u8, result, "f32: 1.1\n"));
868 {867 }
869 var buf1: [32]u8 = undefined;868 {
870 const value: f32 = 1234.567;869 var buf1: [32]u8 = undefined;
871 const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);870 const value: f32 = 1234.567;
872 assert(mem.eql(u8, result, "f32: 1234.57\n"));871 const result = try bufPrint(buf1[0..], "f32: {.2}\n", value);
873 }872 assert(mem.eql(u8, result, "f32: 1234.57\n"));
874 {873 }
875 var buf1: [32]u8 = undefined;874 {
876 const value: f32 = -11.1234;875 var buf1: [32]u8 = undefined;
877 const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);876 const value: f32 = -11.1234;
878 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).877 const result = try bufPrint(buf1[0..], "f32: {.4}\n", value);
879 // -11.12339... is rounded back up to -11.1234878 // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64).
880 assert(mem.eql(u8, result, "f32: -11.1234\n"));879 // -11.12339... is rounded back up to -11.1234
881 }880 assert(mem.eql(u8, result, "f32: -11.1234\n"));
882 {881 }
883 var buf1: [32]u8 = undefined;882 {
884 const value: f32 = 91.12345;883 var buf1: [32]u8 = undefined;
885 const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);884 const value: f32 = 91.12345;
886 assert(mem.eql(u8, result, "f32: 91.12345\n"));885 const result = try bufPrint(buf1[0..], "f32: {.5}\n", value);
887 }886 assert(mem.eql(u8, result, "f32: 91.12345\n"));
888 {887 }
889 var buf1: [32]u8 = undefined;888 {
890 const value: f64 = 91.12345678901235;889 var buf1: [32]u8 = undefined;
891 const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);890 const value: f64 = 91.12345678901235;
892 assert(mem.eql(u8, result, "f64: 91.1234567890\n"));891 const result = try bufPrint(buf1[0..], "f64: {.10}\n", value);
893 }892 assert(mem.eql(u8, result, "f64: 91.1234567890\n"));
894 {893 }
895 var buf1: [32]u8 = undefined;894 {
896 const value: f64 = 0.0;895 var buf1: [32]u8 = undefined;
897 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);896 const value: f64 = 0.0;
898 assert(mem.eql(u8, result, "f64: 0.00000\n"));897 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
899 }898 assert(mem.eql(u8, result, "f64: 0.00000\n"));
900 {899 }
901 var buf1: [32]u8 = undefined;900 {
902 const value: f64 = 5.700;901 var buf1: [32]u8 = undefined;
903 const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);902 const value: f64 = 5.700;
904 assert(mem.eql(u8, result, "f64: 6\n"));903 const result = try bufPrint(buf1[0..], "f64: {.0}\n", value);
905 }904 assert(mem.eql(u8, result, "f64: 6\n"));
906 {905 }
907 var buf1: [32]u8 = undefined;906 {
908 const value: f64 = 9.999;907 var buf1: [32]u8 = undefined;
909 const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);908 const value: f64 = 9.999;
910 assert(mem.eql(u8, result, "f64: 10.0\n"));909 const result = try bufPrint(buf1[0..], "f64: {.1}\n", value);
911 }910 assert(mem.eql(u8, result, "f64: 10.0\n"));
912 {911 }
913 var buf1: [32]u8 = undefined;912 {
914 const value: f64 = 1.0;913 var buf1: [32]u8 = undefined;
915 const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);914 const value: f64 = 1.0;
916 assert(mem.eql(u8, result, "f64: 1.000\n"));915 const result = try bufPrint(buf1[0..], "f64: {.3}\n", value);
917 }916 assert(mem.eql(u8, result, "f64: 1.000\n"));
918 {917 }
919 var buf1: [32]u8 = undefined;918 {
920 const value: f64 = 0.0003;919 var buf1: [32]u8 = undefined;
921 const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);920 const value: f64 = 0.0003;
922 assert(mem.eql(u8, result, "f64: 0.00030000\n"));921 const result = try bufPrint(buf1[0..], "f64: {.8}\n", value);
923 }922 assert(mem.eql(u8, result, "f64: 0.00030000\n"));
924 {923 }
925 var buf1: [32]u8 = undefined;924 {
926 const value: f64 = 1.40130e-45;925 var buf1: [32]u8 = undefined;
927 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);926 const value: f64 = 1.40130e-45;
928 assert(mem.eql(u8, result, "f64: 0.00000\n"));927 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
929 }928 assert(mem.eql(u8, result, "f64: 0.00000\n"));
930 {929 }
931 var buf1: [32]u8 = undefined;930 {
932 const value: f64 = 9.999960e-40;931 var buf1: [32]u8 = undefined;
933 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);932 const value: f64 = 9.999960e-40;
934 assert(mem.eql(u8, result, "f64: 0.00000\n"));933 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
935 }934 assert(mem.eql(u8, result, "f64: 0.00000\n"));
936 // libc checks935 }
937 {936 // libc checks
938 var buf1: [32]u8 = undefined;937 {
939 const value: f64 = f64(@bitCast(f32, u32(916964781)));938 var buf1: [32]u8 = undefined;
940 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);939 const value: f64 = f64(@bitCast(f32, u32(916964781)));
941 assert(mem.eql(u8, result, "f64: 0.00001\n"));940 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
942 }941 assert(mem.eql(u8, result, "f64: 0.00001\n"));
943 {942 }
944 var buf1: [32]u8 = undefined;943 {
945 const value: f64 = f64(@bitCast(f32, u32(925353389)));944 var buf1: [32]u8 = undefined;
946 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);945 const value: f64 = f64(@bitCast(f32, u32(925353389)));
947 assert(mem.eql(u8, result, "f64: 0.00001\n"));946 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
948 }947 assert(mem.eql(u8, result, "f64: 0.00001\n"));
949 {948 }
950 var buf1: [32]u8 = undefined;949 {
951 const value: f64 = f64(@bitCast(f32, u32(1036831278)));950 var buf1: [32]u8 = undefined;
952 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);951 const value: f64 = f64(@bitCast(f32, u32(1036831278)));
953 assert(mem.eql(u8, result, "f64: 0.10000\n"));952 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
954 }953 assert(mem.eql(u8, result, "f64: 0.10000\n"));
955 {954 }
956 var buf1: [32]u8 = undefined;955 {
957 const value: f64 = f64(@bitCast(f32, u32(1065353133)));956 var buf1: [32]u8 = undefined;
958 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);957 const value: f64 = f64(@bitCast(f32, u32(1065353133)));
959 assert(mem.eql(u8, result, "f64: 1.00000\n"));958 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
960 }959 assert(mem.eql(u8, result, "f64: 1.00000\n"));
961 {960 }
962 var buf1: [32]u8 = undefined;961 {
963 const value: f64 = f64(@bitCast(f32, u32(1092616192)));962 var buf1: [32]u8 = undefined;
964 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);963 const value: f64 = f64(@bitCast(f32, u32(1092616192)));
965 assert(mem.eql(u8, result, "f64: 10.00000\n"));964 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
966 }965 assert(mem.eql(u8, result, "f64: 10.00000\n"));
967 // libc differences966 }
968 {967 // libc differences
969 var buf1: [32]u8 = undefined;968 {
970 // This is 0.015625 exactly according to gdb. We thus round down,969 var buf1: [32]u8 = undefined;
971 // however glibc rounds up for some reason. This occurs for all970 // This is 0.015625 exactly according to gdb. We thus round down,
972 // floats of the form x.yyyy25 on a precision point.971 // however glibc rounds up for some reason. This occurs for all
973 const value: f64 = f64(@bitCast(f32, u32(1015021568)));972 // floats of the form x.yyyy25 on a precision point.
974 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);973 const value: f64 = f64(@bitCast(f32, u32(1015021568)));
975 assert(mem.eql(u8, result, "f64: 0.01563\n"));974 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
976 }975 assert(mem.eql(u8, result, "f64: 0.01563\n"));
977976 }
978 // std-windows-x86_64-Debug-bare test case fails977 // std-windows-x86_64-Debug-bare test case fails
979 {978 {
980 // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3979 // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3
981 // also rounds to 630 so I'm inclined to believe libc is not980 // also rounds to 630 so I'm inclined to believe libc is not
982 // optimal here.981 // optimal here.
983 var buf1: [32]u8 = undefined;982 var buf1: [32]u8 = undefined;
984 const value: f64 = f64(@bitCast(f32, u32(1518338049)));983 const value: f64 = f64(@bitCast(f32, u32(1518338049)));
985 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);984 const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
986 assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));985 assert(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
987 }
988 }986 }
989}987}
990988