├── .gitignore ├── .gitmodules ├── README.md ├── build.sh ├── clean.sh └── src ├── internal.zig ├── internal ├── common.zig ├── intrinsics.zig └── tables_digits.zig ├── ryu.zig ├── ryu32 ├── print_shortest.zig └── test_shortest.zig ├── ryu64 ├── parse.zig ├── print_fixed.zig ├── print_hexadecimal.zig ├── print_scientific.zig ├── print_shortest.zig ├── tables_fixed_and_scientific.zig ├── tables_shortest.zig ├── test_fixed.zig ├── test_scientific.zig └── test_shortest.zig └── ryu_c.zig /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | bench-reference* 3 | bench-zig* 4 | zig-cache/ 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ryu"] 2 | path = ryu 3 | url = https://github.com/ulfjack/ryu 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Conversion of https://github.com/ulfjack/ryu to [zig](https://ziglang.org/). 2 | 3 | ## Install 4 | 5 | ``` 6 | git clone --recurse-submodules https://github.com/tiehuis/zig-ryu 7 | ``` 8 | 9 | ## Benchmarks 10 | 11 | Requires `sh`, `make`, `cmake`, `c++`, `zig` 12 | 13 | ``` 14 | ./build.sh 15 | ./bench-reference # reference timing 16 | ./bench-zig # zig timing 17 | ``` 18 | 19 | ## Todo 20 | 21 | - [x] Make more idiomatic 22 | - [x] Add f16 variant 23 | - [x] Add f128 variant (Use partial table set) 24 | - [x] Add specified precision argument (current errol does this after but this is 25 | slows things down a fair bit). 26 | - [x] Benchmark against current float printing code (memory 27 | consumption/performance). 28 | 29 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | mkdir -p build 6 | 7 | echo "building double-conversion dependency" 8 | cd ryu/third_party/double-conversion 9 | mkdir -p build-ryu 10 | cd build-ryu 11 | cmake .. -DCMAKE_BUILD_TYPE=Release > /dev/null 12 | make -j4 > /dev/null 13 | cp double-conversion/libdouble-conversion.a ../../../../build 14 | cd ../../../.. 15 | 16 | echo "building reference benchmark" 17 | g++ -std=c++11 -O2 -Iryu ryu/ryu/*.c ryu/ryu/benchmark/benchmark.cc build/libdouble-conversion.a -o bench-reference 18 | g++ -std=c++11 -O2 -Iryu ryu/ryu/*.c ryu/ryu/benchmark/benchmark_fixed.cc build/libdouble-conversion.a -o bench-reference-fixed 19 | 20 | echo "building zig benchmark" 21 | zig build-obj src/ryu_c.zig --release-fast --output-dir build --name ryu.zig --cache-dir build --library c 22 | g++ -std=c++11 -O2 -Iryu build/ryu.zig.o ryu/ryu/benchmark/benchmark.cc build/libdouble-conversion.a -o bench-zig 23 | g++ -std=c++11 -O2 -Iryu build/ryu.zig.o ryu/ryu/benchmark/benchmark_fixed.cc build/libdouble-conversion.a -o bench-zig-fixed 24 | -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -rf ryu/third_party/double-conversion/build-ryu 4 | rm -rf build 5 | rm -f bench-reference 6 | rm -f bench-zig 7 | -------------------------------------------------------------------------------- /src/internal.zig: -------------------------------------------------------------------------------- 1 | pub usingnamespace @import("internal/tables_digits.zig"); 2 | pub usingnamespace @import("internal/common.zig"); 3 | pub usingnamespace @import("internal/intrinsics.zig"); 4 | -------------------------------------------------------------------------------- /src/internal/common.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | usingnamespace @import("tables_digits.zig"); 4 | 5 | pub const DOUBLE_MANTISSA_BITS = 52; 6 | pub const DOUBLE_EXPONENT_BITS = 11; 7 | pub const DOUBLE_BIAS = 1023; 8 | 9 | pub fn copy_special_string(result: []u8, sign: bool, mantissa: u64) []u8 { 10 | if (mantissa != 0) { 11 | std.mem.copy(u8, result, "nan"); 12 | return result[0..3]; 13 | } 14 | 15 | if (sign) { 16 | result[0] = '-'; 17 | } 18 | 19 | const offset: usize = @boolToInt(sign); 20 | 21 | std.mem.copy(u8, result[offset..], "Infinity"); 22 | return result[0 .. offset + 8]; 23 | } 24 | 25 | pub inline fn append_d_digits(result: []u8, olength: u32, digits_: u32) void { 26 | var digits = digits_; 27 | 28 | var i: usize = 0; 29 | while (digits >= 10000) { 30 | const c = digits % 10000; 31 | digits /= 10000; 32 | const c0 = (c % 100) << 1; 33 | const c1 = (c / 100) << 1; 34 | std.mem.copy(u8, result[olength + 1 - i - 2 ..], DIGIT_TABLE[c0 .. c0 + 2]); 35 | std.mem.copy(u8, result[olength + 1 - i - 4 ..], DIGIT_TABLE[c1 .. c1 + 2]); 36 | i += 4; 37 | } 38 | if (digits >= 100) { 39 | const c = (digits % 100) << 1; 40 | digits /= 100; 41 | std.mem.copy(u8, result[olength + 1 - i - 2 ..], DIGIT_TABLE[c .. c + 2]); 42 | i += 2; 43 | } 44 | if (digits >= 10) { 45 | const c = digits << 1; 46 | result[2] = DIGIT_TABLE[c + 1]; 47 | result[1] = '.'; 48 | result[0] = DIGIT_TABLE[c]; 49 | } else { 50 | result[1] = '.'; 51 | result[0] = '0' + @intCast(u8, digits); 52 | } 53 | } 54 | 55 | pub inline fn append_c_digits(result: []u8, count: u32, digits_: u32) void { 56 | var digits = digits_; 57 | 58 | var i: usize = 0; 59 | while (i < count - 1) : (i += 2) { 60 | const c = (digits % 100) << 1; 61 | digits /= 100; 62 | std.mem.copy(u8, result[count - i - 2 ..], DIGIT_TABLE[c .. c + 2]); 63 | } 64 | if (i < count) { 65 | const c = '0' + @intCast(u8, digits % 10); 66 | result[count - i - 1] = c; 67 | } 68 | } 69 | 70 | pub inline fn append_n_digits(result: []u8, olength: u32, digits_: u32) void { 71 | var digits = digits_; 72 | 73 | var i: usize = 0; 74 | while (digits >= 10000) { 75 | const c = digits % 10000; 76 | digits /= 10000; 77 | const c0 = (c % 100) << 1; 78 | const c1 = (c / 100) << 1; 79 | std.mem.copy(u8, result[olength - i - 2 ..], DIGIT_TABLE[c0 .. c0 + 2]); 80 | std.mem.copy(u8, result[olength - i - 4 ..], DIGIT_TABLE[c1 .. c1 + 2]); 81 | i += 4; 82 | } 83 | if (digits >= 100) { 84 | const c = (digits % 100) << 1; 85 | digits /= 100; 86 | std.mem.copy(u8, result[olength - i - 2 ..], DIGIT_TABLE[c .. c + 2]); 87 | i += 2; 88 | } 89 | if (digits >= 10) { 90 | const c = digits << 1; 91 | std.mem.copy(u8, result[olength - i - 2 ..], DIGIT_TABLE[c .. c + 2]); 92 | } else { 93 | result[0] = '0' + @intCast(u8, digits); 94 | } 95 | } 96 | 97 | pub inline fn append_nine_digits(result: []u8, digits_: u32) void { 98 | var digits = digits_; 99 | 100 | if (digits == 0) { 101 | std.mem.set(u8, result[0..9], '0'); 102 | return; 103 | } 104 | 105 | var i: usize = 0; 106 | while (i < 5) : (i += 4) { 107 | const c = digits % 10000; 108 | digits /= 10000; 109 | const c0 = (c % 100) << 1; 110 | const c1 = (c / 100) << 1; 111 | std.mem.copy(u8, result[7 - i ..], DIGIT_TABLE[c0 .. c0 + 2]); 112 | std.mem.copy(u8, result[5 - i ..], DIGIT_TABLE[c1 .. c1 + 2]); 113 | } 114 | 115 | result[0] = '0' + @intCast(u8, digits); 116 | } 117 | -------------------------------------------------------------------------------- /src/internal/intrinsics.zig: -------------------------------------------------------------------------------- 1 | //! Low-level math intrinsics used during float printing. These may tuned with certain 2 | //! constraints and mind and should not be considered general purpose. 3 | 4 | const std = @import("std"); 5 | 6 | /// Return the length of a number in base-10. 7 | /// 8 | /// Input must be less than 10 digits: 9 | /// f2s: 9 digits are sufficient for round-tripping. 10 | /// d2fixed: We print 9-digit blocks. 11 | pub inline fn decimalLength9(x: u32) u32 { 12 | std.debug.assert(x < 1000000000); 13 | if (x >= 100000000) return 9; 14 | if (x >= 10000000) return 8; 15 | if (x >= 1000000) return 7; 16 | if (x >= 100000) return 6; 17 | if (x >= 10000) return 5; 18 | if (x >= 1000) return 4; 19 | if (x >= 100) return 3; 20 | if (x >= 10) return 2; 21 | return 1; 22 | } 23 | 24 | /// Return the length of a number in base-10. 25 | /// 26 | /// Input must be less than 17 digits. 27 | pub inline fn decimalLength17(x: u64) u32 { 28 | // This is slightly faster than a loop. 29 | // The average output length is 16.38 digits, so we check high-to-low. 30 | // Function precondition: v is not an 18, 19, or 20-digit number. 31 | // (17 digits are sufficient for round-tripping.) 32 | std.debug.assert(x < 100000000000000000); 33 | if (x >= 10000000000000000) return 17; 34 | if (x >= 1000000000000000) return 16; 35 | if (x >= 100000000000000) return 15; 36 | if (x >= 10000000000000) return 14; 37 | if (x >= 1000000000000) return 13; 38 | if (x >= 100000000000) return 12; 39 | if (x >= 10000000000) return 11; 40 | if (x >= 1000000000) return 10; 41 | if (x >= 100000000) return 9; 42 | if (x >= 10000000) return 8; 43 | if (x >= 1000000) return 7; 44 | if (x >= 100000) return 6; 45 | if (x >= 10000) return 5; 46 | if (x >= 1000) return 4; 47 | if (x >= 100) return 3; 48 | if (x >= 10) return 2; 49 | return 1; 50 | } 51 | 52 | /// Returns floor(log_10(2^x)) where 0 <= x <= 1650. 53 | pub inline fn log10Pow2(x: u32) u32 { 54 | std.debug.assert(x >= 0); 55 | std.debug.assert(x <= 1650); 56 | return (x *% 78913) >> 18; 57 | } 58 | 59 | /// Returns floor(log_10(5^x)) where 0 <= x <= 2620 60 | pub inline fn log10Pow5(x: u32) u32 { 61 | std.debug.assert(x >= 0); 62 | std.debug.assert(x <= 2620); 63 | return (x *% 732923) >> 20; 64 | } 65 | 66 | /// Returns log_2(5^x) where 0 <= x <= 3528. 67 | pub inline fn log2pow5(x: u32) u32 { 68 | std.debug.assert(x >= 0); 69 | std.debug.assert(x <= 3528); 70 | return (x *% 1217359) >> 19; 71 | } 72 | 73 | /// Returns ceil(log_2(5^e)) where 0 <= x <= 3528 74 | pub inline fn pow5Bits(x: u32) u32 { 75 | std.debug.assert(x >= 0); 76 | std.debug.assert(x <= 3528); 77 | return ((x *% 1217359) >> 19) + 1; 78 | } 79 | 80 | /// Returns ceil(log_2(5^x)) where 0 <= x <= 3528. 81 | pub inline fn ceil_log2pow5(x: u32) u32 { 82 | return log2pow5(x) + 1; 83 | } 84 | 85 | /// Returns true if 2^p | x for some integer p. 86 | pub inline fn multipleOfPowerOf2(x: u64, p: u32) bool { 87 | std.debug.assert(x != 0); 88 | return x & (std.math.shl(u64, 1, p) -% 1) == 0; 89 | } 90 | 91 | /// Returns the number of factors of 5 in x. 92 | pub inline fn pow5Factor(x: u64) u32 { 93 | var y = x; 94 | var c: u32 = 0; 95 | while (true) : (c += 1) { 96 | std.debug.assert(y != 0); 97 | const q = y / 5; 98 | const r = y - 5 * q; 99 | if (r != 0) break; 100 | y = q; 101 | } 102 | 103 | return c; 104 | } 105 | 106 | /// Returns true if 5^p | x for some integer p. 107 | pub inline fn multipleOfPowerOf5(x: u64, p: u32) bool { 108 | return pow5Factor(x) >= p; 109 | } 110 | 111 | /// Performs a 128x128 bit multiplication. The high bits of the 256-bit product are stored 112 | /// in the input productHi. The low bits are returned directly from the function. 113 | pub inline fn umul256(a: u128, bHi: u64, bLo: u64, productHi: *u128) u128 { 114 | const aLo = @truncate(u64, a); 115 | const aHi = @truncate(u64, a >> 64); 116 | 117 | const b00 = @as(u128, aLo) *% bLo; 118 | const b01 = @as(u128, aLo) *% bHi; 119 | const b10 = @as(u128, aHi) *% bLo; 120 | const b11 = @as(u128, aHi) *% bHi; 121 | 122 | const b00Lo = @truncate(u64, b00); 123 | const b00Hi = @truncate(u64, b00 >> 64); 124 | 125 | const mid1 = b10 +% b00Hi; 126 | const mid1Lo = @truncate(u64, mid1); 127 | const mid1Hi = @truncate(u64, mid1 >> 64); 128 | 129 | const mid2 = b01 +% mid1Lo; 130 | const mid2Lo = @truncate(u64, mid2); 131 | const mid2Hi = @truncate(u64, mid2 >> 64); 132 | 133 | const pHi = b11 +% mid1Hi +% mid2Hi; 134 | const pLo = (@as(u128, mid2Lo) << 64) | b00Lo; 135 | 136 | productHi.* = pHi; 137 | return pLo; 138 | } 139 | 140 | /// Returns the high 128-bits from a 128x128 bit multiplication. 141 | pub inline fn umul256_hi(a: u128, bHi: u64, bLo: u64) u128 { 142 | var hi: u128 = undefined; 143 | _ = umul256(a, bHi, bLo, &hi); 144 | return hi; 145 | } 146 | 147 | /// Returns x % 1000000000. 148 | pub inline fn uint128_mod1e9(x: u128) u32 { 149 | const multiplied = @truncate(u64, umul256_hi(x, 0x89705F4136B4A597, 0x31680A88F8953031)); 150 | const shifted = @truncate(u32, multiplied >> 29); 151 | return @truncate(u32, x) -% 1000000000 *% shifted; 152 | } 153 | 154 | pub inline fn mulShift_mod1e9(m: u64, mul: [3]u64, j: u32) u32 { 155 | const m1 = @as(u128, m); 156 | const b0 = m1 *% mul[0]; 157 | const b1 = m1 *% mul[1]; 158 | const b2 = m1 *% mul[2]; 159 | 160 | std.debug.assert(j >= 128); 161 | std.debug.assert(j <= 180); 162 | 163 | const mid = b1 +% @intCast(u64, b0 >> 64); 164 | const s1 = b2 +% @intCast(u64, mid >> 64); 165 | return uint128_mod1e9(s1 >> @intCast(u7, j - 128)); 166 | } 167 | 168 | pub inline fn mulShift64(m: u64, mul: [2]u64, j: u32) u64 { 169 | const m1 = @as(u128, m); 170 | const b0 = m1 *% mul[0]; 171 | const b2 = m1 *% mul[1]; 172 | return @truncate(u64, ((b0 >> 64) +% b2) >> @intCast(u7, j - 64)); 173 | } 174 | 175 | const POW10_ADDITIONAL_BITS = 120; 176 | 177 | pub inline fn indexForExponent(e: u32) u32 { 178 | return (e + 15) / 16; 179 | } 180 | 181 | pub inline fn pow10BitsForIndex(idx: u32) u32 { 182 | return 16 * idx + POW10_ADDITIONAL_BITS; 183 | } 184 | 185 | pub inline fn lengthForIndex(idx: u32) u32 { 186 | // +1 for ceil, +16 for mantissa, +8 to round up when dividing by 9 187 | return (log10Pow2(16 * idx) + 1 + 16 + 8) / 9; 188 | } 189 | -------------------------------------------------------------------------------- /src/internal/tables_digits.zig: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | // zig fmt: off 19 | 20 | /// A table of all two-digit numbers. This is used to speed up decimal digit 21 | /// generation by copying pairs of digits into the final output. 22 | pub const DIGIT_TABLE = [200]u8 { 23 | '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', 24 | '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', 25 | '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', 26 | '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', 27 | '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', 28 | '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', 29 | '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', 30 | '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', 31 | '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', 32 | '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' 33 | }; 34 | 35 | // zig fmt: on 36 | -------------------------------------------------------------------------------- /src/ryu.zig: -------------------------------------------------------------------------------- 1 | pub const ryu64 = struct { 2 | pub const max_buf_size = struct { 3 | // TODO: Some of these bounds can be tightened 4 | pub const scientific = 2000; 5 | pub const fixed = 2000; 6 | pub const hex = 32; 7 | pub const shortest = 25; 8 | }; 9 | 10 | pub const printScientific = @import("ryu64/print_scientific.zig").printScientific; 11 | pub const printFixed = @import("ryu64/print_fixed.zig").printFixed; 12 | pub const printHex = @import("ryu64/print_hex.zig").printHex; 13 | pub const printShortest = @import("ryu64/print_shortest.zig").printShortest; 14 | }; 15 | 16 | pub const ryu32 = struct { 17 | pub const max_buf_size = struct { 18 | // TODO: Some of these bounds can be tightened 19 | pub const scientific = 2000; 20 | pub const fixed = 2000; 21 | pub const hex = 32; 22 | pub const shortest = 16; 23 | }; 24 | 25 | pub inline fn printScientific(result: []u8, d: f32, precision: u32) []u8 { 26 | return ryu64.printScientific(result, @floatCast(f64, d), precision); 27 | } 28 | 29 | pub inline fn printFixed(result: []u8, d: f32, precision: u32) []u8 { 30 | return ryu64.printFixed(result, @floatCast(f64, d), precision); 31 | } 32 | 33 | pub inline fn printHex(result: []u8, d: f32, precision: u32) []u8 { 34 | return ryu64.printHex(result, @floatCast(f32, d), precision); 35 | } 36 | 37 | pub const printShortest = @import("ryu32/print_shortest.zig").printShortest32; 38 | }; 39 | 40 | pub const ryu16 = struct { 41 | pub const max_buf_size = struct { 42 | // TODO: Some of these bounds can be tightened 43 | pub const scientific = 2000; 44 | pub const fixed = 2000; 45 | pub const hex = 32; 46 | pub const shortest = 16; 47 | }; 48 | 49 | pub inline fn printScientific(result: []u8, d: f16, precision: u32) []u8 { 50 | return ryu64.printScientific(result, @floatCast(f64, d), precision); 51 | } 52 | 53 | pub inline fn printFixed(result: []u8, d: f16, precision: u32) []u8 { 54 | return ryu64.printFixed(result, @floatCast(f64, d), precision); 55 | } 56 | 57 | pub inline fn printHex(result: []u8, d: f16, precision: u32) []u8 { 58 | return ryu64.printHex(result, @floatCast(f32, d), precision); 59 | } 60 | 61 | pub const printShortest = @import("ryu32/print_shortest.zig").printShortest16; 62 | }; 63 | 64 | pub inline fn ryu(comptime T: type) type { 65 | return switch (T) { 66 | f16 => ryu16, 67 | f32 => ryu32, 68 | f64 => ryu64, 69 | else => @compileError("ryu cannot print the type: " ++ @typeName(T)), 70 | }; 71 | } 72 | 73 | test "all" { 74 | _ = @import("ryu64/test_shortest.zig"); 75 | _ = @import("ryu64/test_fixed.zig"); 76 | _ = @import("ryu64/test_scientific.zig"); 77 | _ = @import("ryu64/parse.zig"); 78 | 79 | _ = @import("ryu32/test_shortest.zig"); 80 | } 81 | -------------------------------------------------------------------------------- /src/ryu32/print_shortest.zig: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | const std = @import("std"); 19 | 20 | usingnamespace @import("../internal.zig"); 21 | 22 | // This table is generated by PrintFloatLookupTable. 23 | const FLOAT_POW5_INV_BITCOUNT = 59; 24 | const FLOAT_POW5_INV_SPLIT = [_]u64{ 25 | 576460752303423489, 461168601842738791, 368934881474191033, 295147905179352826, 26 | 472236648286964522, 377789318629571618, 302231454903657294, 483570327845851670, 27 | 386856262276681336, 309485009821345069, 495176015714152110, 396140812571321688, 28 | 316912650057057351, 507060240091291761, 405648192073033409, 324518553658426727, 29 | 519229685853482763, 415383748682786211, 332306998946228969, 531691198313966350, 30 | 425352958651173080, 340282366920938464, 544451787073501542, 435561429658801234, 31 | 348449143727040987, 557518629963265579, 446014903970612463, 356811923176489971, 32 | 570899077082383953, 456719261665907162, 365375409332725730, 33 | }; 34 | 35 | const FLOAT_POW5_BITCOUNT = 61; 36 | const FLOAT_POW5_SPLIT = [_]u64{ 37 | 1152921504606846976, 1441151880758558720, 1801439850948198400, 2251799813685248000, 38 | 1407374883553280000, 1759218604441600000, 2199023255552000000, 1374389534720000000, 39 | 1717986918400000000, 2147483648000000000, 1342177280000000000, 1677721600000000000, 40 | 2097152000000000000, 1310720000000000000, 1638400000000000000, 2048000000000000000, 41 | 1280000000000000000, 1600000000000000000, 2000000000000000000, 1250000000000000000, 42 | 1562500000000000000, 1953125000000000000, 1220703125000000000, 1525878906250000000, 43 | 1907348632812500000, 1192092895507812500, 1490116119384765625, 1862645149230957031, 44 | 1164153218269348144, 1455191522836685180, 1818989403545856475, 2273736754432320594, 45 | 1421085471520200371, 1776356839400250464, 2220446049250313080, 1387778780781445675, 46 | 1734723475976807094, 2168404344971008868, 1355252715606880542, 1694065894508600678, 47 | 2117582368135750847, 1323488980084844279, 1654361225106055349, 2067951531382569187, 48 | 1292469707114105741, 1615587133892632177, 2019483917365790221, 49 | }; 50 | 51 | fn mulShift(m: u32, factor: u64, shift: i32) u32 { 52 | std.debug.assert(shift > 32); 53 | 54 | const factor_lo = @truncate(u32, factor); 55 | const factor_hi = @intCast(u32, factor >> 32); 56 | const bits0 = @as(u64, m) * factor_lo; 57 | const bits1 = @as(u64, m) * factor_hi; 58 | 59 | const sum = (bits0 >> 32) + bits1; 60 | const shifted_sum = sum >> @intCast(u6, shift - 32); 61 | return @intCast(u32, shifted_sum); 62 | } 63 | 64 | fn mulPow5InvDivPow2(m: u32, q: u32, j: i32) u32 { 65 | return mulShift(m, FLOAT_POW5_INV_SPLIT[q], j); 66 | } 67 | 68 | fn mulPow5DivPow2(m: u32, i: u32, j: i32) u32 { 69 | return mulShift(m, FLOAT_POW5_SPLIT[i], j); 70 | } 71 | 72 | const Decimal32 = struct { 73 | sign: bool, 74 | mantissa: u32, 75 | exponent: i32, 76 | }; 77 | 78 | pub fn printShortest16(result: []u8, f: f16) []u8 { 79 | std.debug.assert(result.len >= 11); 80 | const mantissa_bits = std.math.floatMantissaBits(f16); 81 | const exponent_bits = std.math.floatExponentBits(f16); 82 | 83 | const bits = @bitCast(u16, f); 84 | const v = floatToDecimal(bits, mantissa_bits, exponent_bits, false); 85 | const index = decimalToBuffer(v, result); 86 | return result[0..index]; 87 | } 88 | 89 | pub fn printShortest32(result: []u8, f: f32) []u8 { 90 | std.debug.assert(result.len >= 16); 91 | const mantissa_bits = std.math.floatMantissaBits(f32); 92 | const exponent_bits = std.math.floatExponentBits(f32); 93 | 94 | const bits = @bitCast(u32, f); 95 | const v = floatToDecimal(bits, mantissa_bits, exponent_bits, false); 96 | const index = decimalToBuffer(v, result); 97 | return result[0..index]; 98 | } 99 | 100 | pub fn copySpecialString(result: []u8, d: Decimal32) usize { 101 | if (d.mantissa != 0) { 102 | std.mem.copy(u8, result, "NaN"); 103 | return 3; 104 | } 105 | if (d.sign) { 106 | result[0] = '-'; 107 | } 108 | 109 | const offset: usize = @boolToInt(d.sign); 110 | std.mem.copy(u8, result[offset..], "Infinity"); 111 | return offset + 8; 112 | } 113 | 114 | fn floatToDecimal(bits: u32, mantissa_bits: u5, exponent_bits: u5, explicit_leading_bit: bool) Decimal32 { 115 | const exponent_bias = (@as(u32, 1) << (exponent_bits - 1)) - 1; 116 | const sign = ((bits >> (mantissa_bits + exponent_bits)) & 1) != 0; 117 | const mantissa = bits & ((@as(u32, 1) << mantissa_bits) - 1); 118 | const exponent = (bits >> mantissa_bits) & ((@as(u32, 1) << exponent_bits) - 1); 119 | 120 | // Filter out special case nan and inf 121 | if (exponent == 0 and mantissa == 0) { 122 | return Decimal32{ 123 | .sign = sign, 124 | .mantissa = 0, 125 | .exponent = 0, 126 | }; 127 | } 128 | if (exponent == ((@as(u32, 1) << exponent_bits) - 1)) { 129 | return Decimal32{ 130 | .sign = sign, 131 | .mantissa = if (explicit_leading_bit) mantissa & ((@as(u32, 1) << (mantissa_bits - 1)) - 1) else mantissa, 132 | .exponent = 0x7fffffff, 133 | }; 134 | } 135 | 136 | var e2: i32 = undefined; 137 | var m2: u32 = undefined; 138 | 139 | // We subtract 2 so that the bounds computation has 2 additional bits. 140 | if (explicit_leading_bit) { 141 | // mantissa includes the explicit leading bit, so we need to correct for that here 142 | if (exponent == 0) { 143 | e2 = 1 - @intCast(i32, exponent_bias) - @intCast(i32, mantissa_bits) + 1 - 2; 144 | } else { 145 | e2 = @intCast(i32, exponent) - @intCast(i32, exponent_bias) - @intCast(i32, mantissa_bits) + 1 - 2; 146 | } 147 | m2 = mantissa; 148 | } else { 149 | if (exponent == 0) { 150 | e2 = 1 - @intCast(i32, exponent_bias) - @intCast(i32, mantissa_bits) - 2; 151 | m2 = mantissa; 152 | } else { 153 | e2 = @intCast(i32, exponent) - @intCast(i32, exponent_bias) - @intCast(i32, mantissa_bits) - 2; 154 | m2 = (@as(u32, 1) << mantissa_bits) | mantissa; 155 | } 156 | } 157 | 158 | const even = m2 & 1 == 0; 159 | const accept_bounds = even; 160 | 161 | // Step 2: Determine the interval of legal decimal representations. 162 | const mv = 4 * m2; 163 | const mp = 4 * m2 + 2; 164 | // Implicit bool -> int conversion. True is 1, false is 0. 165 | const mm_shift = mantissa != 0 or exponent <= 1; 166 | const mm = 4 * m2 - 1 - @boolToInt(mm_shift); 167 | 168 | // Step 3: Convert to a decimal power base using 64-bit arithmetic. 169 | var vr: u32 = undefined; 170 | var vp: u32 = undefined; 171 | var vm: u32 = undefined; 172 | var e10: i32 = undefined; 173 | var vm_is_trailing_zeros = false; 174 | var vr_is_trailing_zeros = false; 175 | var last_removed_digit: u8 = 0; 176 | 177 | if (e2 >= 0) { 178 | const q = @intCast(i32, log10Pow2(@intCast(u32, e2))); 179 | e10 = q; 180 | const k = FLOAT_POW5_INV_BITCOUNT + pow5Bits(@intCast(u32, q)) - 1; 181 | const i = -e2 + @intCast(i32, q) + @intCast(i32, k); 182 | vr = mulPow5InvDivPow2(mv, @intCast(u32, q), i); 183 | vp = mulPow5InvDivPow2(mp, @intCast(u32, q), i); 184 | vm = mulPow5InvDivPow2(mm, @intCast(u32, q), i); 185 | 186 | if (q != 0 and ((vp - 1) / 10 <= vm / 10)) { 187 | // We need to know one removed digit even if we are not going to loop below. We could use 188 | // q = X - 1 above, except that would require 33 bits for the result, and we've found that 189 | // 32-bit arithmetic is faster even on 64-bit machines. 190 | const l = FLOAT_POW5_INV_BITCOUNT + pow5Bits(@intCast(u32, q - 1)) - 1; 191 | last_removed_digit = @intCast(u8, (mulPow5InvDivPow2(mv, @intCast(u32, q - 1), -e2 + @intCast(i32, q) - 1 + @intCast(i32, l)) % 10)); 192 | } 193 | if (q <= 9) { 194 | // The largest power of 5 that fits in 24 bits is 5^10, but q<=9 seems to be safe as well. 195 | // Only one of mp, mv, and mm can be a multiple of 5, if any. 196 | if (mv % 5 == 0) { 197 | vr_is_trailing_zeros = multipleOfPowerOf5(mv, @intCast(u32, q)); 198 | } else if (accept_bounds) { 199 | vm_is_trailing_zeros = multipleOfPowerOf5(mm, @intCast(u32, q)); 200 | } else { 201 | vp -= @boolToInt(multipleOfPowerOf5(mp, @intCast(u32, q))); 202 | } 203 | } 204 | } else { 205 | const q = @intCast(i32, log10Pow5(@intCast(u32, -e2))); 206 | e10 = q + e2; 207 | const i = -e2 - q; 208 | const k = @intCast(i32, pow5Bits(@intCast(u32, i))) - FLOAT_POW5_BITCOUNT; 209 | var j = q - @intCast(i32, k); 210 | vr = mulPow5DivPow2(mv, @intCast(u32, i), j); 211 | vp = mulPow5DivPow2(mp, @intCast(u32, i), j); 212 | vm = mulPow5DivPow2(mm, @intCast(u32, i), j); 213 | 214 | if (q != 0 and ((vp - 1) / 10 <= vm / 10)) { 215 | j = @intCast(i32, q) - 1 - (@intCast(i32, pow5Bits(@intCast(u32, i + 1))) - @intCast(i32, FLOAT_POW5_BITCOUNT)); 216 | last_removed_digit = @intCast(u8, mulPow5DivPow2(mv, @intCast(u32, i + 1), j) % 10); 217 | } 218 | if (q <= 1) { 219 | // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits. 220 | // mv = 4 * m2, so it always has at least two trailing 0 bits. 221 | vr_is_trailing_zeros = true; 222 | if (accept_bounds) { 223 | // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1. 224 | vm_is_trailing_zeros = mm_shift; 225 | } else { 226 | // mp = mv + 2, so it always has at least one trailing 0 bit. 227 | vp -= 1; 228 | } 229 | } else if (q < 31) { // TODO(ulfjack): Use a tighter bound here. 230 | vr_is_trailing_zeros = (mv & ((@as(u32, 1) << @intCast(u5, (q - 1))) - 1)) == 0; 231 | } 232 | } 233 | 234 | // Step 4: Find the shortest decimal representation in the interval of legal representations. 235 | var removed: u32 = 0; 236 | var output: u32 = undefined; 237 | if (vm_is_trailing_zeros or vr_is_trailing_zeros) { 238 | // General case, which happens rarely. 239 | while (vp / 10 > vm / 10) { 240 | vm_is_trailing_zeros = vm_is_trailing_zeros and vm % 10 == 0; 241 | vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; 242 | last_removed_digit = @intCast(u8, vr % 10); 243 | vr /= 10; 244 | vp /= 10; 245 | vm /= 10; 246 | removed += 1; 247 | } 248 | if (vm_is_trailing_zeros) { 249 | while (vm % 10 == 0) { 250 | vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; 251 | last_removed_digit = @intCast(u8, vr % 10); 252 | vr /= 10; 253 | vp /= 10; 254 | vm /= 10; 255 | removed += 1; 256 | } 257 | } 258 | if (vr_is_trailing_zeros and (last_removed_digit == 5) and (vr % 2 == 0)) { 259 | // Round even if the exact number is .....50..0. 260 | last_removed_digit = 4; 261 | } 262 | // We need to take vr+1 if vr is outside bounds or we need to round up. 263 | output = vr + 264 | @boolToInt((vr == vm and (!accept_bounds or !vm_is_trailing_zeros)) or (last_removed_digit >= 5)); 265 | } else { 266 | // Common case. 267 | while (vp / 10 > vm / 10) { 268 | last_removed_digit = @intCast(u8, vr % 10); 269 | vr /= 10; 270 | vp /= 10; 271 | vm /= 10; 272 | removed += 1; 273 | } 274 | // We need to take vr+1 if vr is outside bounds or we need to round up. 275 | output = vr + @boolToInt((vr == vm) or (last_removed_digit >= 5)); 276 | } 277 | 278 | return Decimal32{ 279 | .sign = sign, 280 | .mantissa = output, 281 | .exponent = e10 + @intCast(i32, removed), 282 | }; 283 | } 284 | 285 | fn decimalToBuffer(v: Decimal32, result: []u8) usize { 286 | if (v.exponent == 0x7fffffff) { 287 | return copySpecialString(result, v); 288 | } 289 | 290 | // Step 5: Print the decimal representation. 291 | var index: usize = 0; 292 | if (v.sign) { 293 | result[index] = '-'; 294 | index += 1; 295 | } 296 | 297 | var output = v.mantissa; 298 | const olength = decimalLength9(output); 299 | 300 | // Print the decimal digits. The following code is equivalent to: 301 | // 302 | // var i: usize = 0; 303 | // while (i < olength - 1) : (i += 1) { 304 | // const c = output % 10; 305 | // output /= 10; 306 | // result[index + olength - i] = @intCast(u8, '0' + c); 307 | // } 308 | // result[index] = @intCast(u8, '0' + output % 10); 309 | var i: usize = 0; 310 | while (output >= 10000) { 311 | const c = output % 10000; 312 | output /= 10000; 313 | const c0 = (c % 100) << 1; 314 | const c1 = (c / 100) << 1; 315 | 316 | // TODO: See https://github.com/ziglang/zig/issues/1329 317 | result[index + olength - i - 1 + 0] = DIGIT_TABLE[c0 + 0]; 318 | result[index + olength - i - 1 + 1] = DIGIT_TABLE[c0 + 1]; 319 | result[index + olength - i - 3 + 0] = DIGIT_TABLE[c1 + 0]; 320 | result[index + olength - i - 3 + 1] = DIGIT_TABLE[c1 + 1]; 321 | i += 4; 322 | } 323 | if (output >= 100) { 324 | const c = (output % 100) << 1; 325 | output /= 100; 326 | 327 | result[index + olength - i - 1 + 0] = DIGIT_TABLE[c + 0]; 328 | result[index + olength - i - 1 + 1] = DIGIT_TABLE[c + 1]; 329 | i += 2; 330 | } 331 | if (output >= 10) { 332 | const c = output << 1; 333 | result[index + olength - i] = DIGIT_TABLE[c + 1]; 334 | result[index] = DIGIT_TABLE[c]; 335 | } else { 336 | result[index] = @intCast(u8, '0' + output); 337 | } 338 | 339 | // Print decimal point if needed. 340 | if (olength > 1) { 341 | result[index + 1] = '.'; 342 | index += olength + 1; 343 | } else { 344 | index += 1; 345 | } 346 | 347 | // Print the exponent. 348 | result[index] = 'E'; 349 | var exp = v.exponent + @intCast(i32, olength) - 1; 350 | index += 1; 351 | if (exp < 0) { 352 | result[index] = '-'; 353 | index += 1; 354 | exp = -exp; 355 | } 356 | 357 | var expu = @intCast(usize, exp); 358 | 359 | if (exp >= 10) { 360 | result[index + 0] = DIGIT_TABLE[2 * expu + 0]; 361 | result[index + 1] = DIGIT_TABLE[2 * expu + 1]; 362 | index += 2; 363 | } else { 364 | result[index] = @intCast(u8, '0' + expu); 365 | index += 1; 366 | } 367 | 368 | return index; 369 | } 370 | -------------------------------------------------------------------------------- /src/ryu32/test_shortest.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ryu32 = @import("../ryu.zig").ryu32; 3 | 4 | fn testShortest(expected: []const u8, input: f32) void { 5 | var buffer: [ryu32.max_buf_size.shortest]u8 = undefined; 6 | const converted = ryu32.printShortest(buffer[0..], input); 7 | std.debug.assert(std.mem.eql(u8, expected, converted)); 8 | } 9 | 10 | test "basic" { 11 | testShortest("0E0", 0.0); 12 | testShortest("-0E0", -@as(f32, 0.0)); 13 | testShortest("1E0", 1.0); 14 | testShortest("-1E0", -1.0); 15 | testShortest("NaN", std.math.nan(f32)); 16 | testShortest("Infinity", std.math.inf(f32)); 17 | testShortest("-Infinity", -std.math.inf(f32)); 18 | } 19 | 20 | test "switch to subnormal" { 21 | testShortest("1.1754944E-38", 1.1754944e-38); 22 | } 23 | 24 | test "min and max" { 25 | testShortest("3.4028235E38", @bitCast(f32, @as(u32, 0x7f7fffff))); 26 | testShortest("1E-45", @bitCast(f32, @as(u32, 1))); 27 | } 28 | 29 | // Check that we return the exact boundary if it is the shortest 30 | // representation, but only if the original floating point number is even. 31 | test "boundary round even" { 32 | testShortest("3.355445E7", 3.355445e7); 33 | testShortest("9E9", 8.999999e9); 34 | testShortest("3.436672E10", 3.4366717e10); 35 | } 36 | 37 | // If the exact value is exactly halfway between two shortest representations, 38 | // then we round to even. It seems like this only makes a difference if the 39 | // last two digits are ...2|5 or ...7|5, and we cut off the 5. 40 | test "exact value round even" { 41 | testShortest("3.0540412E5", 3.0540412E5); 42 | testShortest("8.0990312E3", 8.0990312E3); 43 | } 44 | 45 | test "lots of trailing zeros" { 46 | // Pattern for the first test: 00111001100000000000000000000000 47 | testShortest("2.4414062E-4", 2.4414062E-4); 48 | testShortest("2.4414062E-3", 2.4414062E-3); 49 | testShortest("4.3945312E-3", 4.3945312E-3); 50 | testShortest("6.3476562E-3", 6.3476562E-3); 51 | } 52 | 53 | test "looks like pow5" { 54 | // These numbers have a mantissa that is the largest power of 5 that fits, 55 | // and an exponent that causes the computation for q to result in 10, which is a corner 56 | // case for Ryu. 57 | testShortest("6.7108864E17", @bitCast(f32, @as(u32, 0x5D1502F9))); 58 | testShortest("1.3421773E18", @bitCast(f32, @as(u32, 0x5D9502F9))); 59 | testShortest("2.6843546E18", @bitCast(f32, @as(u32, 0x5E1502F9))); 60 | } 61 | 62 | test "regression" { 63 | testShortest("4.7223665E21", 4.7223665E21); 64 | testShortest("8.388608E6", 8388608.0); 65 | testShortest("1.6777216E7", 1.6777216E7); 66 | testShortest("3.3554436E7", 3.3554436E7); 67 | testShortest("6.7131496E7", 6.7131496E7); 68 | testShortest("1.9310392E-38", 1.9310392E-38); 69 | testShortest("-2.47E-43", -2.47E-43); 70 | testShortest("1.993244E-38", 1.993244E-38); 71 | testShortest("4.1039004E3", 4103.9003); 72 | testShortest("5.3399997E9", 5.3399997E9); 73 | testShortest("6.0898E-39", 6.0898E-39); 74 | testShortest("1.0310042E-3", 0.0010310042); 75 | testShortest("2.882326E17", 2.8823261E17); 76 | testShortest("7.038531E-26", 7.0385309E-26); 77 | testShortest("9.223404E17", 9.2234038E17); 78 | testShortest("6.710887E7", 6.7108872E7); 79 | testShortest("1E-44", 1.0E-44); 80 | testShortest("2.816025E14", 2.816025E14); 81 | testShortest("9.223372E18", 9.223372E18); 82 | testShortest("1.5846086E29", 1.5846085E29); 83 | testShortest("1.1811161E19", 1.1811161E19); 84 | testShortest("5.368709E18", 5.368709E18); 85 | testShortest("4.6143166E18", 4.6143165E18); 86 | testShortest("7.812537E-3", 0.007812537); 87 | testShortest("1E-45", 1.4E-45); 88 | testShortest("1.18697725E20", 1.18697724E20); 89 | testShortest("1.00014165E-36", 1.00014165E-36); 90 | testShortest("2E2", 200.0); 91 | testShortest("3.3554432E7", 3.3554432E7); 92 | } 93 | -------------------------------------------------------------------------------- /src/ryu64/parse.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | usingnamespace struct { 4 | pub usingnamespace @import("../internal.zig"); 5 | pub usingnamespace @import("tables_shortest.zig"); 6 | }; 7 | 8 | inline fn floor_log2(x: u64) u32 { 9 | return 63 - @clz(u64, x); 10 | } 11 | 12 | pub const ParseError = error{ 13 | TooShort, 14 | TooLong, 15 | Malformed, 16 | }; 17 | 18 | pub fn parse(s: []const u8) ParseError!f64 { 19 | if (s.len == 0) { 20 | return error.TooShort; 21 | } 22 | 23 | var m10digits: usize = 0; 24 | var e10digits: usize = 0; 25 | var dot_index = s.len; 26 | var e_index = s.len; 27 | var m10: u64 = 0; 28 | var e10: i32 = 0; 29 | var signed_m = false; 30 | var signed_e = false; 31 | 32 | var i: usize = 0; 33 | 34 | if (s[i] == '-') { 35 | signed_m = true; 36 | i += 1; 37 | } 38 | 39 | while (i < s.len) : (i += 1) { 40 | const c = s[i]; 41 | if (c == '.') { 42 | if (dot_index != s.len) { 43 | return error.Malformed; 44 | } 45 | dot_index = i; 46 | continue; 47 | } 48 | if (c < '0' or c > '9') { 49 | break; 50 | } 51 | if (m10digits >= 17) { 52 | return error.TooLong; 53 | } 54 | m10 = 10 * m10 + (c - '0'); 55 | if (m10 != 0) { 56 | m10digits += 1; 57 | } 58 | } 59 | 60 | if (i < s.len and (s[i] == 'e' or s[i] == 'E')) { 61 | e_index = i; 62 | i += 1; 63 | if (i < s.len and (s[i] == '-' or s[i] == '+')) { 64 | signed_e = s[i] == '-'; 65 | i += 1; 66 | } 67 | 68 | while (i < s.len) : (i += 1) { 69 | const c = s[i]; 70 | if (c < '0' or c > '9') { 71 | return error.Malformed; 72 | } 73 | if (e10digits > 3) { 74 | // TODO: Be more lenient, return +-inf or +-0 instead. 75 | return error.TooLong; 76 | } 77 | e10 = 10 * e10 + @intCast(i32, c - '0'); 78 | if (e10 != 0) { 79 | e10digits += 1; 80 | } 81 | } 82 | } 83 | 84 | if (i < s.len) { 85 | return error.Malformed; 86 | } 87 | 88 | if (signed_e) { 89 | e10 = -e10; 90 | } 91 | e10 -= if (dot_index < e_index) @intCast(i32, e_index - dot_index - 1) else 0; 92 | if (m10 == 0) { 93 | return if (signed_m) -0.0 else 0.0; 94 | } 95 | 96 | if (@intCast(i32, m10digits) + e10 <= -324 or m10 == 0) { 97 | // Number is less than 1e-324, which should be rounded down to 0 98 | const ieee = @as(u64, @boolToInt(signed_m)) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS); 99 | return @bitCast(f64, ieee); 100 | } 101 | if (@intCast(i32, m10digits) + e10 >= 310) { 102 | // Number is larger than 1e+309, which should be rounded to inf 103 | const ieee = (@as(u64, @boolToInt(signed_m)) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS)) | (0x7ff << DOUBLE_MANTISSA_BITS); 104 | return @bitCast(f64, ieee); 105 | } 106 | 107 | // Convert to binary float m2 * 2^e2, while retaining information about whether conversion was 108 | // exact (trailing_zeros). 109 | var e2: i32 = undefined; 110 | var m2: u64 = undefined; 111 | var trailing_zeros: bool = undefined; 112 | 113 | if (e10 >= 0) { 114 | // The length of m * 10^e in bits is: 115 | // log2(m10 * 10^e10) = log2(m10) + e10 log2(10) = log2(m10) + e10 + e10 * log2(5) 116 | // 117 | // We want to compute the DOUBLE_MANTISSA_BITS + 1 top-most bits (+1 for the implicit leading 118 | // one in IEEE format). We therefore choose a binary output exponent of 119 | // log2(m10 * 10^e10) - (DOUBLE_MANTISSA_BITS + 1). 120 | // 121 | // We use floor(log2(5^e10)) so that we get at least this many bits; better to 122 | // have an additional bit than to not have enough bits. 123 | e2 = @intCast(i32, floor_log2(m10)) + e10 + @intCast(i32, log2pow5(@intCast(u32, e10))) - (DOUBLE_MANTISSA_BITS + 1); 124 | 125 | // We now compute [m10 * 10^e10 / 2^e2] = [m10 * 5^e10 / 2^(e2-e10)]. 126 | // To that end, we use the DOUBLE_POW5_SPLIT table. 127 | const j = e2 - e10 - @intCast(i32, ceil_log2pow5(@intCast(u32, e10))) + DOUBLE_POW5_BITCOUNT; 128 | std.debug.assert(j >= 0); 129 | std.debug.assert(e10 < DOUBLE_POW5_TABLE_SIZE); 130 | m2 = mulShift64(m10, DOUBLE_POW5_SPLIT[@intCast(usize, e10)], @intCast(u32, j)); 131 | 132 | // We also compute if the result is exact, i.e., 133 | // [m10 * 10^e10 / 2^e2] == m10 * 10^e10 / 2^e2. 134 | // This can only be the case if 2^e2 divides m10 * 10^e10, which in turn requires that the 135 | // largest power of 2 that divides m10 + e10 is greater than e2. If e2 is less than e10, then 136 | // the result must be exact. Otherwise we use the existing multipleOfPowerOf2 function. 137 | trailing_zeros = e2 < e10 or multipleOfPowerOf2(m10, @intCast(u32, e2 - e10)); 138 | } else { 139 | e2 = @intCast(i32, floor_log2(m10)) + e10 - @intCast(i32, ceil_log2pow5(@intCast(u32, -e10))) - (DOUBLE_MANTISSA_BITS + 1); 140 | const j = e2 - e10 + @intCast(i32, ceil_log2pow5(@intCast(u32, -e10))) - 1 + DOUBLE_POW5_INV_BITCOUNT; 141 | std.debug.assert(-e10 < DOUBLE_POW5_INV_TABLE_SIZE); 142 | m2 = mulShift64(m10, DOUBLE_POW5_INV_SPLIT[@intCast(u32, -e10)], @intCast(u32, j)); 143 | trailing_zeros = multipleOfPowerOf5(m10, @intCast(u32, -e10)); 144 | } 145 | 146 | // Compute the final IEEE exponent 147 | var ieee_e2: i32 = std.math.max(0, e2 + DOUBLE_BIAS + @intCast(i32, floor_log2(m2))); 148 | 149 | if (ieee_e2 > 0x7fe) { 150 | // Final IEEE exponent is larger than the maximum representable, +-infinity 151 | const ieee = (@as(u64, @boolToInt(signed_m)) << (DOUBLE_EXPONENT_BITS + DOUBLE_MANTISSA_BITS)) | (0x7ff << DOUBLE_MANTISSA_BITS); 152 | return @bitCast(f64, ieee); 153 | } 154 | 155 | // We need to figure out how much we need to shift m2. The tricky part is that we need to take 156 | // the final IEEE exponent into account, so we need to reverse the bias and also special-case 157 | // the value 0. 158 | const shift = (if (ieee_e2 == 0) 1 else ieee_e2) - e2 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS; 159 | std.debug.assert(shift >= 0); 160 | 161 | // We need to round up if the exact value is more than 0.5 above the value we computed. That's 162 | // equivalent to checking if the last removed bit was 1 and either the value was not just 163 | // trailing zeros or the result would otherwise be odd. 164 | // 165 | // We need to update trailingZeros given that we have the exact output exponent ieee_e2 now. 166 | trailing_zeros = trailing_zeros and ((m2 & (@as(u64, 1) << @intCast(u6, shift - 1)) - 1)) == 0; 167 | const last_removed_bit = (m2 >> @intCast(u6, shift - 1)) & 1; 168 | const round_up = last_removed_bit != 0 and (!trailing_zeros or (((m2 >> @intCast(u6, shift)) & 1) != 0)); 169 | 170 | var ieee_m2 = (m2 >> @intCast(u6, shift)) + @boolToInt(round_up); 171 | if (ieee_m2 == (1 << (DOUBLE_MANTISSA_BITS + 1))) { 172 | // Due to how the IEEE represents +-inf, we don't need to check for overflow here 173 | ieee_e2 += 1; 174 | } 175 | ieee_m2 &= (1 << DOUBLE_MANTISSA_BITS) - 1; 176 | 177 | const ieee = (@as(u64, @boolToInt(signed_m)) << DOUBLE_EXPONENT_BITS) | (@intCast(u64, ieee_e2) << DOUBLE_MANTISSA_BITS) | ieee_m2; 178 | return @bitCast(f64, ieee); 179 | } 180 | 181 | fn expectParse(expected: f64, x: []const u8) void { 182 | std.testing.expectEqual(parse(x) catch unreachable, expected); 183 | } 184 | 185 | fn expectError(expected: anyerror, x: []const u8) void { 186 | std.testing.expectError(expected, parse(x)); 187 | } 188 | 189 | test "bad input" { 190 | expectError(error.Malformed, "x"); 191 | expectError(error.Malformed, "1..1"); 192 | expectError(error.Malformed, ".."); 193 | expectError(error.Malformed, "1ee1"); 194 | expectError(error.Malformed, "1e.1"); 195 | expectError(error.TooShort, ""); 196 | expectError(error.TooLong, "123456789012345678"); 197 | expectError(error.TooLong, "1e12345"); 198 | } 199 | 200 | test "basic" { 201 | expectParse(0.0, "0"); 202 | expectParse(-0.0, "-0"); 203 | expectParse(1.0, "1"); 204 | expectParse(2.0, "2"); 205 | expectParse(123456789.0, "123456789"); 206 | expectParse(123.456, "123.456"); 207 | expectParse(123.456, "123456e-3"); 208 | expectParse(123.456, "1234.56e-1"); 209 | expectParse(1.453, "1.453"); 210 | expectParse(1453.0, "1.453e+3"); 211 | expectParse(0.0, ".0"); 212 | expectParse(1.0, "1e0"); 213 | expectParse(1.0, "1E0"); 214 | expectParse(1.0, "000001.000000"); 215 | } 216 | 217 | test "min/max" { 218 | expectParse(1.7976931348623157e308, "1.7976931348623157e308"); 219 | expectParse(5E-324, "5E-324"); 220 | } 221 | 222 | test "mantissa rounding overflow" { 223 | // This results in binary mantissa that is all ones and requires rounding up 224 | // because it is closer to 1 than to the next smaller float. This is a 225 | // regression test that the mantissa overflow is handled correctly by 226 | // increasing the exponent. 227 | expectParse(1.0, "0.99999999999999999"); 228 | // This number overflows the mantissa *and* the IEEE exponent. 229 | expectParse(std.math.inf(f64), "1.7976931348623159e308"); 230 | } 231 | 232 | test "underflow" { 233 | expectParse(0.0, "2.4e-324"); 234 | expectParse(0.0, "1e-324"); 235 | expectParse(0.0, "9.99999e-325"); 236 | // These are just about halfway between 0 and the smallest float. 237 | // The first is just below the halfway point, the second just above. 238 | expectParse(0.0, "2.4703282292062327e-324"); 239 | expectParse(5e-324, "2.4703282292062328e-324"); 240 | } 241 | 242 | test "overflow" { 243 | expectParse(std.math.inf(f64), "2e308"); 244 | expectParse(std.math.inf(f64), "1e309"); 245 | } 246 | 247 | test "table size denormal" { 248 | expectParse(5e-324, "4.9406564584124654e-324"); 249 | } 250 | -------------------------------------------------------------------------------- /src/ryu64/print_fixed.zig: -------------------------------------------------------------------------------- 1 | //! Print an f64 as a fixed-precision. 2 | 3 | const std = @import("std"); 4 | 5 | usingnamespace struct { 6 | pub usingnamespace @import("../internal.zig"); 7 | pub usingnamespace @import("tables_fixed_and_scientific.zig"); 8 | }; 9 | 10 | /// Print an f64 in fixed-precision format. Result must be at least XXX bytes but need not be 11 | /// more, as this is the upper limit. 12 | pub fn printFixed(result: []u8, d: f64, precision: u32) []u8 { 13 | const bits = @bitCast(u64, d); 14 | 15 | const ieee_sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0; 16 | const ieee_mantissa = bits & ((1 << DOUBLE_MANTISSA_BITS) - 1); 17 | const ieee_exponent = (bits >> DOUBLE_MANTISSA_BITS) & ((1 << DOUBLE_EXPONENT_BITS) - 1); 18 | 19 | if (ieee_exponent == ((1 << DOUBLE_EXPONENT_BITS) - 1)) { 20 | return copy_special_string(result, ieee_sign, ieee_mantissa); 21 | } 22 | 23 | if (ieee_exponent == 0 and ieee_mantissa == 0) { 24 | var index: usize = 0; 25 | if (ieee_sign) { 26 | result[index] = '-'; 27 | index += 1; 28 | } 29 | 30 | result[index] = '0'; 31 | index += 1; 32 | 33 | if (precision > 0) { 34 | result[index] = '.'; 35 | index += 1; 36 | std.mem.set(u8, result[index .. index + precision], '0'); 37 | index += precision; 38 | } 39 | 40 | return result[0..index]; 41 | } 42 | 43 | var e2: i32 = undefined; 44 | var m2: u64 = undefined; 45 | if (ieee_exponent == 0) { 46 | e2 = 1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS; 47 | m2 = ieee_mantissa; 48 | } else { 49 | e2 = @intCast(i32, ieee_exponent) - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS; 50 | m2 = (1 << DOUBLE_MANTISSA_BITS) | ieee_mantissa; 51 | } 52 | 53 | var index: usize = 0; 54 | var nonzero = false; 55 | if (ieee_sign) { 56 | result[index] = '-'; 57 | index += 1; 58 | } 59 | 60 | if (e2 >= -52) { 61 | const idx = if (e2 < 0) 0 else indexForExponent(@intCast(u32, e2)); 62 | const p10bits = pow10BitsForIndex(idx); 63 | const len = lengthForIndex(idx); 64 | 65 | var i_: usize = 0; 66 | while (i_ < len) : (i_ += 1) { 67 | const i = len - i_ - 1; 68 | const j = @intCast(u32, @intCast(i32, p10bits) - e2); 69 | const digits = mulShift_mod1e9(m2 << 8, POW10_SPLIT[POW10_OFFSET[idx] + i], j + 8); 70 | 71 | if (nonzero) { 72 | append_nine_digits(result[index..], digits); 73 | index += 9; 74 | } else if (digits != 0) { 75 | const olength = decimalLength9(digits); 76 | append_n_digits(result[index..], olength, digits); 77 | index += olength; 78 | nonzero = true; 79 | } 80 | } 81 | } 82 | if (!nonzero) { 83 | result[index] = '0'; 84 | index += 1; 85 | } 86 | if (precision > 0) { 87 | result[index] = '.'; 88 | index += 1; 89 | } 90 | 91 | const RoundUp = enum { 92 | Never = 0, 93 | Always, 94 | Odd, 95 | }; 96 | 97 | if (e2 < 0) { 98 | const idx = @intCast(u32, -e2) / 16; 99 | const blocks = precision / 9 + 1; 100 | var round_up = RoundUp.Never; 101 | 102 | var i: u32 = 0; 103 | if (blocks <= MIN_BLOCK_2[idx]) { 104 | i = blocks; 105 | std.mem.set(u8, result[index .. index + precision], '0'); 106 | index += precision; 107 | } else if (i < MIN_BLOCK_2[idx]) { 108 | i = MIN_BLOCK_2[idx]; 109 | std.mem.set(u8, result[index .. index + 9 * i], '0'); 110 | index += 9 * i; 111 | } 112 | 113 | while (i < blocks) : (i += 1) { 114 | const j = ADDITIONAL_BITS_2 + @intCast(u32, -e2) - 16 * idx; 115 | const p = POW10_OFFSET_2[idx] + i - MIN_BLOCK_2[idx]; 116 | if (p >= POW10_OFFSET_2[idx + 1]) { 117 | // If the remaining digits are all 0, then we might as well use memset. 118 | // No rounding required in this case. 119 | const fill = precision - 9 * i; 120 | std.mem.set(u8, result[index .. index + fill], '0'); 121 | index += fill; 122 | break; 123 | } 124 | 125 | var digits = mulShift_mod1e9(m2 << 8, POW10_SPLIT_2[p], j + 8); 126 | 127 | if (i < blocks - 1) { 128 | append_nine_digits(result[index..], digits); 129 | index += 9; 130 | } else { 131 | const maximum = @intCast(i32, precision) - @intCast(i32, 9 * i); 132 | var last_digit: u32 = 0; 133 | 134 | var k: usize = 0; 135 | while (@intCast(i32, k) < 9 - maximum) : (k += 1) { 136 | last_digit = digits % 10; 137 | digits /= 10; 138 | } 139 | 140 | if (last_digit != 5) { 141 | round_up = @intToEnum(RoundUp, @boolToInt(last_digit > 5)); 142 | } else { 143 | // Is m * 10^(additionalDigits + 1) / 2^(-e2) an integer? 144 | const required_twos = -e2 - @intCast(i32, precision) - 1; 145 | const trailing_zeros = required_twos <= 0 or (required_twos < 60 and 146 | multipleOfPowerOf2(m2, @intCast(u32, required_twos))); 147 | round_up = if (trailing_zeros) .Odd else .Always; 148 | } 149 | if (maximum > 0) { 150 | append_c_digits(result[index..], @intCast(u32, maximum), digits); 151 | index += @intCast(u32, maximum); 152 | } 153 | break; 154 | } 155 | } 156 | 157 | if (round_up != .Never) { 158 | var round_index = @intCast(isize, index); 159 | var dot_index: usize = 0; // '.' can't be located at index 0 160 | while (true) { 161 | // When we hit the first round (-1), then do something different 162 | round_index -= 1; 163 | 164 | var c: u8 = undefined; 165 | if (round_index != -1) { 166 | c = result[@intCast(usize, round_index)]; 167 | } 168 | 169 | if (round_index == -1 or c == '-') { 170 | result[@intCast(usize, round_index + 1)] = '1'; 171 | if (dot_index > 0) { 172 | result[dot_index] = '0'; 173 | result[dot_index + 1] = '.'; 174 | } 175 | result[index] = '0'; 176 | index += 1; 177 | break; 178 | } 179 | if (c == '.') { 180 | dot_index = @intCast(usize, round_index); 181 | continue; 182 | } else if (c == '9') { 183 | result[@intCast(usize, round_index)] = '0'; 184 | round_up = .Always; 185 | continue; 186 | } else { 187 | if (round_up == .Odd and c % 2 == 0) { 188 | break; 189 | } 190 | result[@intCast(usize, round_index)] = c + 1; 191 | break; 192 | } 193 | } 194 | } 195 | } else { 196 | std.mem.set(u8, result[index .. index + precision], '0'); 197 | index += precision; 198 | } 199 | 200 | return result[0..index]; 201 | } 202 | -------------------------------------------------------------------------------- /src/ryu64/print_hexadecimal.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | pub fn printHex(result: []u8, d: f64) []u8 { 4 | // TODO 5 | } 6 | -------------------------------------------------------------------------------- /src/ryu64/print_scientific.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | usingnamespace struct { 4 | pub usingnamespace @import("../internal.zig"); 5 | pub usingnamespace @import("tables_fixed_and_scientific.zig"); 6 | }; 7 | 8 | pub fn printScientific(result: []u8, d: f64, precision_: u32) []u8 { 9 | var precision = precision_; 10 | const bits = @bitCast(u64, d); 11 | 12 | const ieee_sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0; 13 | const ieee_mantissa = bits & ((1 << DOUBLE_MANTISSA_BITS) - 1); 14 | const ieee_exponent = (bits >> DOUBLE_MANTISSA_BITS) & ((1 << DOUBLE_EXPONENT_BITS) - 1); 15 | 16 | if (ieee_exponent == ((1 << DOUBLE_EXPONENT_BITS) - 1)) { 17 | return copy_special_string(result, ieee_sign, ieee_mantissa); 18 | } 19 | 20 | if (ieee_exponent == 0 and ieee_mantissa == 0) { 21 | var index: usize = 0; 22 | if (ieee_sign) { 23 | result[index] = '-'; 24 | index += 1; 25 | } 26 | 27 | result[index] = '0'; 28 | index += 1; 29 | 30 | if (precision > 0) { 31 | result[index] = '.'; 32 | index += 1; 33 | std.mem.set(u8, result[index .. index + precision], '0'); 34 | index += precision; 35 | } 36 | 37 | std.mem.copy(u8, result[index .. index + 4], "e+00"); 38 | index += 4; 39 | 40 | return result[0..index]; 41 | } 42 | 43 | var e2: i32 = undefined; 44 | var m2: u64 = undefined; 45 | if (ieee_exponent == 0) { 46 | e2 = 1 - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS; 47 | m2 = ieee_mantissa; 48 | } else { 49 | e2 = @intCast(i32, ieee_exponent) - DOUBLE_BIAS - DOUBLE_MANTISSA_BITS; 50 | m2 = (1 << DOUBLE_MANTISSA_BITS) | ieee_mantissa; 51 | } 52 | 53 | const print_decimal_point = precision > 0; 54 | precision += 1; 55 | 56 | var index: usize = 0; 57 | if (ieee_sign) { 58 | result[index] = '-'; 59 | index += 1; 60 | } 61 | 62 | var digits: u32 = 0; 63 | var printed_digits: u32 = 0; 64 | var available_digits: u32 = 0; 65 | var exp: i32 = 0; 66 | if (e2 >= -52) { 67 | const idx = if (e2 < 0) 0 else indexForExponent(@intCast(u32, e2)); 68 | const p10bits = pow10BitsForIndex(idx); 69 | const len = lengthForIndex(idx); 70 | 71 | var i_: usize = 0; 72 | while (i_ < len) : (i_ += 1) { 73 | const i = len - i_ - 1; 74 | const j = @intCast(u32, @intCast(i32, p10bits) - e2); 75 | 76 | digits = mulShift_mod1e9(m2 << 8, POW10_SPLIT[POW10_OFFSET[idx] + i], j + 8); 77 | if (printed_digits != 0) { 78 | if (printed_digits + 9 > precision) { 79 | available_digits = 9; 80 | break; 81 | } 82 | append_nine_digits(result[index..], digits); 83 | index += 9; 84 | printed_digits += 9; 85 | } else if (digits != 0) { 86 | available_digits = decimalLength9(digits); 87 | exp = @intCast(i32, i * 9 + available_digits) - 1; 88 | if (available_digits > precision) { 89 | break; 90 | } 91 | if (print_decimal_point) { 92 | append_d_digits(result[index..], available_digits, digits); 93 | index += available_digits + 1; // +1 for decimal point 94 | } else { 95 | result[index] = '0' + @intCast(u8, digits); 96 | index += 1; 97 | } 98 | printed_digits = available_digits; 99 | available_digits = 0; 100 | } 101 | } 102 | } 103 | 104 | if (e2 < 0 and available_digits == 0) { 105 | const idx = @intCast(u32, -e2) / 16; 106 | 107 | var i = MIN_BLOCK_2[idx]; 108 | while (i < 200) : (i += 1) { 109 | const j = ADDITIONAL_BITS_2 + (@intCast(u32, -e2) - 16 * idx); 110 | const p = POW10_OFFSET_2[idx] + i - MIN_BLOCK_2[idx]; 111 | digits = if (p >= POW10_OFFSET_2[idx + 1]) 0 else mulShift_mod1e9(m2 << 8, POW10_SPLIT_2[p], j + 8); 112 | 113 | if (printed_digits != 0) { 114 | if (printed_digits + 9 > precision) { 115 | available_digits = 9; 116 | break; 117 | } 118 | append_nine_digits(result[index..], digits); 119 | index += 9; 120 | printed_digits += 9; 121 | } else if (digits != 0) { 122 | available_digits = decimalLength9(digits); 123 | exp = -(@intCast(i32, i) + 1) * 9 + @intCast(i32, available_digits) - 1; 124 | if (available_digits > precision) { 125 | break; 126 | } 127 | if (print_decimal_point) { 128 | append_d_digits(result[index..], available_digits, digits); 129 | index += available_digits + 1; // +1 for decimal point 130 | } else { 131 | result[index] = '0' + @intCast(u8, digits); 132 | index += 1; 133 | } 134 | printed_digits = available_digits; 135 | available_digits = 0; 136 | } 137 | } 138 | } 139 | 140 | const maximum = precision - printed_digits; 141 | 142 | if (available_digits == 0) { 143 | digits = 0; 144 | } 145 | var last_digit: u32 = 0; 146 | if (available_digits > maximum) { 147 | var k: usize = 0; 148 | while (k < available_digits - maximum) : (k += 1) { 149 | last_digit = digits % 10; 150 | digits /= 10; 151 | } 152 | } 153 | 154 | const RoundUp = enum { 155 | Never = 0, 156 | Always, 157 | Odd, 158 | }; 159 | 160 | // 0 = don't round up; 1 = round up unconditionally, 2 = round up if odd 161 | var round_up = RoundUp.Never; 162 | if (last_digit != 5) { 163 | round_up = @intToEnum(RoundUp, @boolToInt(last_digit > 5)); 164 | } else { 165 | // Is m * 2^e2 * 10^(precision + 1 - exp) an integer? 166 | // precision was already increased by 1, so we don't need to write +1 here. 167 | const rexp = @intCast(i32, precision) - exp; 168 | const required_twos = -e2 - rexp; 169 | var trailing_zeros = required_twos <= 0 or 170 | (required_twos < 60 and multipleOfPowerOf2(m2, @intCast(u32, required_twos))); 171 | if (rexp < 0) { 172 | const required_fives = -rexp; 173 | trailing_zeros = trailing_zeros and multipleOfPowerOf5(m2, @intCast(u32, required_fives)); 174 | } 175 | round_up = if (trailing_zeros) .Odd else .Always; 176 | } 177 | if (printed_digits != 0) { 178 | if (digits == 0) { 179 | std.mem.set(u8, result[index .. index + maximum], '0'); 180 | } else { 181 | append_c_digits(result[index..], maximum, digits); 182 | } 183 | index += maximum; 184 | } else { 185 | if (print_decimal_point) { 186 | append_d_digits(result[index..], maximum, digits); 187 | index += maximum + 1; // +1 for decimal point 188 | } else { 189 | result[index] = '0' + @intCast(u8, digits); 190 | index += 1; 191 | } 192 | } 193 | 194 | if (round_up != .Never) { 195 | var round_index = @intCast(i32, index); 196 | while (true) { 197 | round_index -= 1; 198 | 199 | var c: u8 = undefined; 200 | if (round_index != -1) { 201 | c = result[@intCast(usize, round_index)]; 202 | } 203 | 204 | if (round_index == -1 or c == '-') { 205 | result[@intCast(usize, round_index + 1)] = '1'; 206 | exp += 1; 207 | break; 208 | } 209 | if (c == '.') { 210 | continue; 211 | } else if (c == '9') { 212 | result[@intCast(usize, round_index)] = '0'; 213 | round_up = .Always; 214 | continue; 215 | } else { 216 | if (round_up == .Odd and c % 2 == 0) { 217 | break; 218 | } 219 | result[@intCast(usize, round_index)] = c + 1; 220 | break; 221 | } 222 | } 223 | } 224 | 225 | result[index] = 'e'; 226 | index += 1; 227 | if (exp < 0) { 228 | result[index] = '-'; 229 | index += 1; 230 | exp = -exp; 231 | } else { 232 | result[index] = '+'; 233 | index += 1; 234 | } 235 | 236 | const pexp = @intCast(u32, exp); 237 | 238 | if (pexp >= 100) { 239 | const c = pexp % 10; 240 | std.mem.copy(u8, result[index..], DIGIT_TABLE[2 * (pexp / 10) .. 2 * (pexp / 10) + 2]); 241 | result[index + 2] = '0' + @intCast(u8, c); 242 | index += 3; 243 | } else { 244 | std.mem.copy(u8, result[index..], DIGIT_TABLE[2 * pexp .. 2 * pexp + 2]); 245 | index += 2; 246 | } 247 | 248 | return result[0..index]; 249 | } 250 | -------------------------------------------------------------------------------- /src/ryu64/print_shortest.zig: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | const std = @import("std"); 19 | const builtin = @import("builtin"); 20 | 21 | usingnamespace struct { 22 | pub usingnamespace @import("../internal.zig"); 23 | pub usingnamespace @import("tables_shortest.zig"); 24 | }; 25 | 26 | const ryu_optimize_size = false; //builtin.mode == builtin.Mode.ReleaseSmall; 27 | 28 | pub fn mulShift(m: u64, mul: [2]u64, j: i32) u64 { 29 | const b0 = @as(u128, m) * mul[0]; 30 | const b2 = @as(u128, m) * mul[1]; 31 | return @truncate(u64, (((b0 >> 64) + b2) >> @intCast(u7, (j - 64)))); 32 | } 33 | 34 | pub fn mulShiftAll(m: u64, mul: [2]u64, j: i32, vp: *u64, vm: *u64, mm_shift: u32) u64 { 35 | vp.* = mulShift(4 * m + 2, mul, j); 36 | vm.* = mulShift(4 * m - 1 - mm_shift, mul, j); 37 | return mulShift(4 * m, mul, j); 38 | } 39 | 40 | const Decimal64 = struct { 41 | sign: bool, 42 | mantissa: u64, 43 | exponent: i32, 44 | }; 45 | 46 | pub fn printShortest(result: []u8, f: f64) []u8 { 47 | std.debug.assert(result.len >= 25); 48 | 49 | const mantissa_bits = std.math.floatMantissaBits(f64); 50 | const exponent_bits = std.math.floatExponentBits(f64); 51 | 52 | const bits = @bitCast(u64, f); 53 | const v = floatToDecimal(bits, mantissa_bits, exponent_bits, false); 54 | const index = decimalToBuffer(v, result); 55 | return result[0..index]; 56 | } 57 | 58 | fn floatToDecimal(bits: u64, mantissa_bits: u6, exponent_bits: u6, explicit_leading_bit: bool) Decimal64 { 59 | const exponent_bias = (@as(u64, 1) << (exponent_bits - 1)) - 1; 60 | const sign = ((bits >> (mantissa_bits + exponent_bits)) & 1) != 0; 61 | const mantissa = bits & ((@as(u64, 1) << mantissa_bits) - 1); 62 | const exponent = (bits >> mantissa_bits) & ((@as(u64, 1) << exponent_bits) - 1); 63 | 64 | // Filter out special case nan and inf 65 | if (exponent == 0 and mantissa == 0) { 66 | return Decimal64{ 67 | .sign = sign, 68 | .mantissa = 0, 69 | .exponent = 0, 70 | }; 71 | } 72 | if (exponent == ((@as(u64, 1) << exponent_bits) - 1)) { 73 | return Decimal64{ 74 | .sign = sign, 75 | .mantissa = if (explicit_leading_bit) mantissa & ((@as(u64, 1) << (mantissa_bits - 1)) - 1) else mantissa, 76 | .exponent = 0x7fffffff, 77 | }; 78 | } 79 | 80 | var e2: i32 = undefined; 81 | var m2: u64 = undefined; 82 | 83 | // We subtract 2 so that the bounds computation has 2 additional bits. 84 | if (explicit_leading_bit) { 85 | // mantissa includes the explicit leading bit, so we need to correct for that here 86 | if (exponent == 0) { 87 | e2 = 1 - @intCast(i32, exponent_bias) - @intCast(i32, mantissa_bits) + 1 - 2; 88 | } else { 89 | e2 = @intCast(i32, exponent) - @intCast(i32, exponent_bias) - @intCast(i32, mantissa_bits) + 1 - 2; 90 | } 91 | m2 = mantissa; 92 | } else { 93 | if (exponent == 0) { 94 | e2 = 1 - @intCast(i32, exponent_bias) - @intCast(i32, mantissa_bits) - 2; 95 | m2 = mantissa; 96 | } else { 97 | e2 = @intCast(i32, exponent) - @intCast(i32, exponent_bias) - @intCast(i32, mantissa_bits) - 2; 98 | m2 = (@as(u64, 1) << mantissa_bits) | mantissa; 99 | } 100 | } 101 | 102 | const even = m2 & 1 == 0; 103 | const accept_bounds = even; 104 | 105 | // Step 2: Determine the interval of legal decimal representations. 106 | const mv = 4 * m2; 107 | // Implicit bool -> int conversion. True is 1, false is 0. 108 | const mm_shift = mantissa != 0 or exponent <= 1; 109 | // We would compute mp and mm like this: 110 | // uint64_t mp = 4 * m2 + 2; 111 | // uint64_t mm = mv - 1 - mm_shift; 112 | 113 | // Step 3: Convert to a decimal power base using 128-bit arithmetic. 114 | var vr: u64 = undefined; 115 | var vp: u64 = undefined; 116 | var vm: u64 = undefined; 117 | var e10: i32 = undefined; 118 | var vm_is_trailing_zeros = false; 119 | var vr_is_trailing_zeros = false; 120 | 121 | if (e2 >= 0) { 122 | // I tried special-casing q == 0, but there was no effect on performance. 123 | // This expression is slightly faster than max(0, log10Pow2(e2) - 1). 124 | const q = @intCast(i32, log10Pow2(@intCast(u32, e2))) - @intCast(i32, @boolToInt(e2 > 3)); 125 | e10 = q; 126 | const k = DOUBLE_POW5_INV_BITCOUNT + pow5Bits(@intCast(u32, q)) - 1; 127 | const i = -e2 + @intCast(i32, q) + @intCast(i32, k); 128 | 129 | if (ryu_optimize_size) { 130 | var pow5: [2]u64 = undefined; 131 | computeInvPow5(@intCast(u32, q), pow5[0..]); 132 | vr = mulShiftAll(m2, pow5, i, &vp, &vm, @boolToInt(mm_shift)); 133 | } else { 134 | vr = mulShiftAll(m2, DOUBLE_POW5_INV_SPLIT[@intCast(usize, q)], i, &vp, &vm, @boolToInt(mm_shift)); 135 | } 136 | 137 | if (q <= 21) { 138 | // This should use q <= 22, but I think 21 is also safe. Smaller values 139 | // may still be safe, but it's more difficult to reason about them. 140 | // Only one of mp, mv, and mm can be a multiple of 5, if any. 141 | if (mv % 5 == 0) { 142 | vr_is_trailing_zeros = multipleOfPowerOf5(mv, @intCast(u32, q)); 143 | } else if (accept_bounds) { 144 | // Same as min(e2 + (~mm & 1), pow5Factor(mm)) >= q 145 | // <=> e2 + (~mm & 1) >= q && pow5Factor(mm) >= q 146 | // <=> true && pow5Factor(mm) >= q, since e2 >= q. 147 | vm_is_trailing_zeros = multipleOfPowerOf5(mv - 1 - @boolToInt(mm_shift), @intCast(u32, q)); 148 | } else { 149 | // Same as min(e2 + 1, pow5Factor(mp)) >= q. 150 | vp -= @boolToInt(multipleOfPowerOf5(mv + 2, @intCast(u32, q))); 151 | } 152 | } 153 | } else { 154 | // This expression is slightly faster than max(0, log10Pow5(-e2) - 1). 155 | const q = @intCast(i32, log10Pow5(@intCast(u32, -e2))) - @intCast(i32, @boolToInt(-e2 > 1)); 156 | e10 = q + e2; 157 | const i = -e2 - q; 158 | const k = @intCast(i32, pow5Bits(@intCast(u32, i))) - DOUBLE_POW5_BITCOUNT; 159 | const j = q - k; 160 | 161 | if (ryu_optimize_size) { 162 | var pow5: [2]u64 = undefined; 163 | computePow5(@intCast(u32, i), pow5[0..]); 164 | vr = mulShiftAll(m2, pow5, j, &vp, &vm, @boolToInt(mm_shift)); 165 | } else { 166 | vr = mulShiftAll(m2, DOUBLE_POW5_SPLIT[@intCast(usize, i)], j, &vp, &vm, @boolToInt(mm_shift)); 167 | } 168 | 169 | if (q <= 1) { 170 | // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits. 171 | // mv = 4 m2, so it always has at least two trailing 0 bits. 172 | vr_is_trailing_zeros = true; 173 | if (accept_bounds) { 174 | // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1. 175 | vm_is_trailing_zeros = mm_shift; 176 | } else { 177 | vp -= 1; 178 | } 179 | } else if (q < 63) { // TODO(ulfjack): Use a tighter bound here. 180 | // We need to compute min(ntz(mv), pow5Factor(mv) - e2) >= q-1 181 | // <=> ntz(mv) >= q-1 && pow5Factor(mv) - e2 >= q-1 182 | // <=> ntz(mv) >= q-1 (e2 is negative and -e2 >= q) 183 | // <=> (mv & ((1 << (q-1)) - 1)) == 0 184 | // We also need to make sure that the left shift does not overflow. 185 | vr_is_trailing_zeros = (mv & ((@as(u64, 1) << @intCast(u6, q - 1)) - 1)) == 0; 186 | } 187 | } 188 | 189 | // Step 4: Find the shortest decimal representation in the interval of legal representations. 190 | var removed: u32 = 0; 191 | var last_removed_digit: u8 = 0; 192 | var output: u64 = undefined; 193 | // On average, we remove ~2 digits. 194 | if (vm_is_trailing_zeros or vr_is_trailing_zeros) { 195 | // General case, which happens rarely (<1%). 196 | while (vp / 10 > vm / 10) { 197 | vm_is_trailing_zeros = vm_is_trailing_zeros and vm % 10 == 0; 198 | vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; 199 | last_removed_digit = @intCast(u8, vr % 10); 200 | vr /= 10; 201 | vp /= 10; 202 | vm /= 10; 203 | removed += 1; 204 | } 205 | 206 | if (vm_is_trailing_zeros) { 207 | while (vm % 10 == 0) { 208 | vr_is_trailing_zeros = vr_is_trailing_zeros and last_removed_digit == 0; 209 | last_removed_digit = @intCast(u8, vr % 10); 210 | vr /= 10; 211 | vp /= 10; 212 | vm /= 10; 213 | removed += 1; 214 | } 215 | } 216 | 217 | if (vr_is_trailing_zeros and (last_removed_digit == 5) and (vr % 2 == 0)) { 218 | // Round even if the exact numbers is .....50..0. 219 | last_removed_digit = 4; 220 | } 221 | // We need to take vr+1 if vr is outside bounds or we need to round up. 222 | output = vr + 223 | @boolToInt((vr == vm and (!accept_bounds or !vm_is_trailing_zeros)) or (last_removed_digit >= 5)); 224 | } else { 225 | // Specialized for the common case (~99.3%). Percentags below are relative to this. 226 | var round_up = false; 227 | if (vp / 100 > vm / 100) { // Optimization: remove two digits at a time (~86.2%) 228 | round_up = vr % 100 >= 50; 229 | vr /= 100; 230 | vp /= 100; 231 | vm /= 100; 232 | removed += 2; 233 | } 234 | 235 | // Loop iterations below (approximately), without optimization above: 236 | // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02% 237 | // Loop iterations below (approximately), with optimization above: 238 | // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02% 239 | while (vp / 10 > vm / 10) { 240 | round_up = vr % 10 >= 5; 241 | vr /= 10; 242 | vp /= 10; 243 | vm /= 10; 244 | removed += 1; 245 | } 246 | 247 | // We need to take vr+1 if vr is outside bounds or we need to round up. 248 | output = vr + @boolToInt(vr == vm or round_up); 249 | } 250 | 251 | var exp = e10 + @intCast(i32, removed); 252 | 253 | return Decimal64{ 254 | .sign = sign, 255 | .mantissa = output, 256 | .exponent = exp, 257 | }; 258 | } 259 | 260 | pub fn copySpecialString(result: []u8, d: Decimal64) usize { 261 | if (d.mantissa != 0) { 262 | std.mem.copy(u8, result, "NaN"); 263 | return 3; 264 | } 265 | if (d.sign) { 266 | result[0] = '-'; 267 | } 268 | 269 | const offset: usize = @boolToInt(d.sign); 270 | std.mem.copy(u8, result[offset..], "Infinity"); 271 | return offset + 8; 272 | } 273 | 274 | fn decimalToBuffer(v: Decimal64, result: []u8) usize { 275 | if (v.exponent == 0x7fffffff) { 276 | return copySpecialString(result, v); 277 | } 278 | 279 | // Step 5: Print the decimal representation. 280 | var index: usize = 0; 281 | if (v.sign) { 282 | result[index] = '-'; 283 | index += 1; 284 | } 285 | 286 | var output = v.mantissa; 287 | const olength = decimalLength17(output); 288 | 289 | // Print the decimal digits. The following code is equivalent to: 290 | // 291 | // var i: usize = 0; 292 | // while (i < olength - 1) : (i += 1) { 293 | // const c = output % 10; 294 | // output /= 10; 295 | // result[index + olength - i] = @intCast(u8, '0' + c); 296 | // } 297 | // result[index] = @intCast(u8, '0' + output % 10); 298 | var i: usize = 0; 299 | // We prefer 32-bit operations, even on 64-bit platforms. 300 | // We have at most 17 digits, and 32-bit unsigned int can store 9. We cut off 301 | // 8 in the first iteration, so the remainder will fit into a 32-bit int. 302 | if ((output >> 32) != 0) { 303 | var output2 = @truncate(u32, output % 100000000); 304 | output /= 100000000; 305 | 306 | const c = output2 % 10000; 307 | output2 /= 10000; 308 | const d = output2 % 10000; 309 | const c0 = (c % 100) << 1; 310 | const c1 = (c / 100) << 1; 311 | const d0 = (d % 100) << 1; 312 | const d1 = (d / 100) << 1; 313 | 314 | // TODO: See https://github.com/ziglang/zig/issues/1329 315 | result[index + olength - i - 1 + 0] = DIGIT_TABLE[c0 + 0]; 316 | result[index + olength - i - 1 + 1] = DIGIT_TABLE[c0 + 1]; 317 | result[index + olength - i - 3 + 0] = DIGIT_TABLE[c1 + 0]; 318 | result[index + olength - i - 3 + 1] = DIGIT_TABLE[c1 + 1]; 319 | result[index + olength - i - 5 + 0] = DIGIT_TABLE[d0 + 0]; 320 | result[index + olength - i - 5 + 1] = DIGIT_TABLE[d0 + 1]; 321 | result[index + olength - i - 7 + 0] = DIGIT_TABLE[d1 + 0]; 322 | result[index + olength - i - 7 + 1] = DIGIT_TABLE[d1 + 1]; 323 | i += 8; 324 | } 325 | 326 | var output2 = @truncate(u32, output); 327 | while (output2 >= 10000) { 328 | const c = @truncate(u32, output2 % 10000); 329 | output2 /= 10000; 330 | const c0 = (c % 100) << 1; 331 | const c1 = (c / 100) << 1; 332 | 333 | result[index + olength - i - 1 + 0] = DIGIT_TABLE[c0 + 0]; 334 | result[index + olength - i - 1 + 1] = DIGIT_TABLE[c0 + 1]; 335 | result[index + olength - i - 3 + 0] = DIGIT_TABLE[c1 + 0]; 336 | result[index + olength - i - 3 + 1] = DIGIT_TABLE[c1 + 1]; 337 | i += 4; 338 | } 339 | if (output2 >= 100) { 340 | const c = @truncate(u32, (output2 % 100) << 1); 341 | output2 /= 100; 342 | 343 | result[index + olength - i - 1 + 0] = DIGIT_TABLE[c + 0]; 344 | result[index + olength - i - 1 + 1] = DIGIT_TABLE[c + 1]; 345 | i += 2; 346 | } 347 | if (output2 >= 10) { 348 | const c = @truncate(u32, output2 << 1); 349 | // We can't use memcpy here: the decimal dot goes between these two digits. 350 | result[index + olength - i] = DIGIT_TABLE[c + 1]; 351 | result[index] = DIGIT_TABLE[c]; 352 | } else { 353 | result[index] = @intCast(u8, '0' + output2); 354 | } 355 | 356 | // Print decimal point if needed. 357 | if (olength > 1) { 358 | result[index + 1] = '.'; 359 | index += olength + 1; 360 | } else { 361 | index += 1; 362 | } 363 | 364 | // Print the exponent. 365 | result[index] = 'E'; 366 | index += 1; 367 | 368 | var exp = v.exponent + @intCast(i32, olength) - 1; 369 | if (exp < 0) { 370 | result[index] = '-'; 371 | index += 1; 372 | exp = -exp; 373 | } 374 | 375 | const expu = @intCast(usize, exp); 376 | 377 | if (expu >= 100) { 378 | const c = @rem(expu, 10); 379 | const offset2 = @intCast(usize, 2 * @divTrunc(expu, 10)); 380 | 381 | result[index + 0] = DIGIT_TABLE[offset2 + 0]; 382 | result[index + 1] = DIGIT_TABLE[offset2 + 1]; 383 | result[index + 2] = @intCast(u8, '0' + c); 384 | index += 3; 385 | } else if (expu >= 10) { 386 | const offset2 = @intCast(usize, 2 * expu); 387 | 388 | result[index + 0] = DIGIT_TABLE[offset2 + 0]; 389 | result[index + 1] = DIGIT_TABLE[offset2 + 1]; 390 | index += 2; 391 | } else { 392 | result[index] = @intCast(u8, '0' + expu); 393 | index += 1; 394 | } 395 | 396 | return index; 397 | } 398 | -------------------------------------------------------------------------------- /src/ryu64/tables_shortest.zig: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | // These tables are generated by PrintDoubleLookupTable. 19 | pub const DOUBLE_POW5_INV_BITCOUNT = 125; 20 | pub const DOUBLE_POW5_BITCOUNT = 125; 21 | 22 | pub const DOUBLE_POW5_INV_TABLE_SIZE = 342; 23 | pub const DOUBLE_POW5_TABLE_SIZE = 326; 24 | 25 | // zig fmt: off 26 | 27 | pub const DOUBLE_POW5_INV_SPLIT = [DOUBLE_POW5_INV_TABLE_SIZE][2]u64 { 28 | [2]u64{ 1, 2305843009213693952 }, [2]u64{ 11068046444225730970, 1844674407370955161 }, 29 | [2]u64{ 5165088340638674453, 1475739525896764129 }, [2]u64{ 7821419487252849886, 1180591620717411303 }, 30 | [2]u64{ 8824922364862649494, 1888946593147858085 }, [2]u64{ 7059937891890119595, 1511157274518286468 }, 31 | [2]u64{ 13026647942995916322, 1208925819614629174 }, [2]u64{ 9774590264567735146, 1934281311383406679 }, 32 | [2]u64{ 11509021026396098440, 1547425049106725343 }, [2]u64{ 16585914450600699399, 1237940039285380274 }, 33 | [2]u64{ 15469416676735388068, 1980704062856608439 }, [2]u64{ 16064882156130220778, 1584563250285286751 }, 34 | [2]u64{ 9162556910162266299, 1267650600228229401 }, [2]u64{ 7281393426775805432, 2028240960365167042 }, 35 | [2]u64{ 16893161185646375315, 1622592768292133633 }, [2]u64{ 2446482504291369283, 1298074214633706907 }, 36 | [2]u64{ 7603720821608101175, 2076918743413931051 }, [2]u64{ 2393627842544570617, 1661534994731144841 }, 37 | [2]u64{ 16672297533003297786, 1329227995784915872 }, [2]u64{ 11918280793837635165, 2126764793255865396 }, 38 | [2]u64{ 5845275820328197809, 1701411834604692317 }, [2]u64{ 15744267100488289217, 1361129467683753853 }, 39 | [2]u64{ 3054734472329800808, 2177807148294006166 }, [2]u64{ 17201182836831481939, 1742245718635204932 }, 40 | [2]u64{ 6382248639981364905, 1393796574908163946 }, [2]u64{ 2832900194486363201, 2230074519853062314 }, 41 | [2]u64{ 5955668970331000884, 1784059615882449851 }, [2]u64{ 1075186361522890384, 1427247692705959881 }, 42 | [2]u64{ 12788344622662355584, 2283596308329535809 }, [2]u64{ 13920024512871794791, 1826877046663628647 }, 43 | [2]u64{ 3757321980813615186, 1461501637330902918 }, [2]u64{ 10384555214134712795, 1169201309864722334 }, 44 | [2]u64{ 5547241898389809503, 1870722095783555735 }, [2]u64{ 4437793518711847602, 1496577676626844588 }, 45 | [2]u64{ 10928932444453298728, 1197262141301475670 }, [2]u64{ 17486291911125277965, 1915619426082361072 }, 46 | [2]u64{ 6610335899416401726, 1532495540865888858 }, [2]u64{ 12666966349016942027, 1225996432692711086 }, 47 | [2]u64{ 12888448528943286597, 1961594292308337738 }, [2]u64{ 17689456452638449924, 1569275433846670190 }, 48 | [2]u64{ 14151565162110759939, 1255420347077336152 }, [2]u64{ 7885109000409574610, 2008672555323737844 }, 49 | [2]u64{ 9997436015069570011, 1606938044258990275 }, [2]u64{ 7997948812055656009, 1285550435407192220 }, 50 | [2]u64{ 12796718099289049614, 2056880696651507552 }, [2]u64{ 2858676849947419045, 1645504557321206042 }, 51 | [2]u64{ 13354987924183666206, 1316403645856964833 }, [2]u64{ 17678631863951955605, 2106245833371143733 }, 52 | [2]u64{ 3074859046935833515, 1684996666696914987 }, [2]u64{ 13527933681774397782, 1347997333357531989 }, 53 | [2]u64{ 10576647446613305481, 2156795733372051183 }, [2]u64{ 15840015586774465031, 1725436586697640946 }, 54 | [2]u64{ 8982663654677661702, 1380349269358112757 }, [2]u64{ 18061610662226169046, 2208558830972980411 }, 55 | [2]u64{ 10759939715039024913, 1766847064778384329 }, [2]u64{ 12297300586773130254, 1413477651822707463 }, 56 | [2]u64{ 15986332124095098083, 2261564242916331941 }, [2]u64{ 9099716884534168143, 1809251394333065553 }, 57 | [2]u64{ 14658471137111155161, 1447401115466452442 }, [2]u64{ 4348079280205103483, 1157920892373161954 }, 58 | [2]u64{ 14335624477811986218, 1852673427797059126 }, [2]u64{ 7779150767507678651, 1482138742237647301 }, 59 | [2]u64{ 2533971799264232598, 1185710993790117841 }, [2]u64{ 15122401323048503126, 1897137590064188545 }, 60 | [2]u64{ 12097921058438802501, 1517710072051350836 }, [2]u64{ 5988988032009131678, 1214168057641080669 }, 61 | [2]u64{ 16961078480698431330, 1942668892225729070 }, [2]u64{ 13568862784558745064, 1554135113780583256 }, 62 | [2]u64{ 7165741412905085728, 1243308091024466605 }, [2]u64{ 11465186260648137165, 1989292945639146568 }, 63 | [2]u64{ 16550846638002330379, 1591434356511317254 }, [2]u64{ 16930026125143774626, 1273147485209053803 }, 64 | [2]u64{ 4951948911778577463, 2037035976334486086 }, [2]u64{ 272210314680951647, 1629628781067588869 }, 65 | [2]u64{ 3907117066486671641, 1303703024854071095 }, [2]u64{ 6251387306378674625, 2085924839766513752 }, 66 | [2]u64{ 16069156289328670670, 1668739871813211001 }, [2]u64{ 9165976216721026213, 1334991897450568801 }, 67 | [2]u64{ 7286864317269821294, 2135987035920910082 }, [2]u64{ 16897537898041588005, 1708789628736728065 }, 68 | [2]u64{ 13518030318433270404, 1367031702989382452 }, [2]u64{ 6871453250525591353, 2187250724783011924 }, 69 | [2]u64{ 9186511415162383406, 1749800579826409539 }, [2]u64{ 11038557946871817048, 1399840463861127631 }, 70 | [2]u64{ 10282995085511086630, 2239744742177804210 }, [2]u64{ 8226396068408869304, 1791795793742243368 }, 71 | [2]u64{ 13959814484210916090, 1433436634993794694 }, [2]u64{ 11267656730511734774, 2293498615990071511 }, 72 | [2]u64{ 5324776569667477496, 1834798892792057209 }, [2]u64{ 7949170070475892320, 1467839114233645767 }, 73 | [2]u64{ 17427382500606444826, 1174271291386916613 }, [2]u64{ 5747719112518849781, 1878834066219066582 }, 74 | [2]u64{ 15666221734240810795, 1503067252975253265 }, [2]u64{ 12532977387392648636, 1202453802380202612 }, 75 | [2]u64{ 5295368560860596524, 1923926083808324180 }, [2]u64{ 4236294848688477220, 1539140867046659344 }, 76 | [2]u64{ 7078384693692692099, 1231312693637327475 }, [2]u64{ 11325415509908307358, 1970100309819723960 }, 77 | [2]u64{ 9060332407926645887, 1576080247855779168 }, [2]u64{ 14626963555825137356, 1260864198284623334 }, 78 | [2]u64{ 12335095245094488799, 2017382717255397335 }, [2]u64{ 9868076196075591040, 1613906173804317868 }, 79 | [2]u64{ 15273158586344293478, 1291124939043454294 }, [2]u64{ 13369007293925138595, 2065799902469526871 }, 80 | [2]u64{ 7005857020398200553, 1652639921975621497 }, [2]u64{ 16672732060544291412, 1322111937580497197 }, 81 | [2]u64{ 11918976037903224966, 2115379100128795516 }, [2]u64{ 5845832015580669650, 1692303280103036413 }, 82 | [2]u64{ 12055363241948356366, 1353842624082429130 }, [2]u64{ 841837113407818570, 2166148198531886609 }, 83 | [2]u64{ 4362818505468165179, 1732918558825509287 }, [2]u64{ 14558301248600263113, 1386334847060407429 }, 84 | [2]u64{ 12225235553534690011, 2218135755296651887 }, [2]u64{ 2401490813343931363, 1774508604237321510 }, 85 | [2]u64{ 1921192650675145090, 1419606883389857208 }, [2]u64{ 17831303500047873437, 2271371013423771532 }, 86 | [2]u64{ 6886345170554478103, 1817096810739017226 }, [2]u64{ 1819727321701672159, 1453677448591213781 }, 87 | [2]u64{ 16213177116328979020, 1162941958872971024 }, [2]u64{ 14873036941900635463, 1860707134196753639 }, 88 | [2]u64{ 15587778368262418694, 1488565707357402911 }, [2]u64{ 8780873879868024632, 1190852565885922329 }, 89 | [2]u64{ 2981351763563108441, 1905364105417475727 }, [2]u64{ 13453127855076217722, 1524291284333980581 }, 90 | [2]u64{ 7073153469319063855, 1219433027467184465 }, [2]u64{ 11317045550910502167, 1951092843947495144 }, 91 | [2]u64{ 12742985255470312057, 1560874275157996115 }, [2]u64{ 10194388204376249646, 1248699420126396892 }, 92 | [2]u64{ 1553625868034358140, 1997919072202235028 }, [2]u64{ 8621598323911307159, 1598335257761788022 }, 93 | [2]u64{ 17965325103354776697, 1278668206209430417 }, [2]u64{ 13987124906400001422, 2045869129935088668 }, 94 | [2]u64{ 121653480894270168, 1636695303948070935 }, [2]u64{ 97322784715416134, 1309356243158456748 }, 95 | [2]u64{ 14913111714512307107, 2094969989053530796 }, [2]u64{ 8241140556867935363, 1675975991242824637 }, 96 | [2]u64{ 17660958889720079260, 1340780792994259709 }, [2]u64{ 17189487779326395846, 2145249268790815535 }, 97 | [2]u64{ 13751590223461116677, 1716199415032652428 }, [2]u64{ 18379969808252713988, 1372959532026121942 }, 98 | [2]u64{ 14650556434236701088, 2196735251241795108 }, [2]u64{ 652398703163629901, 1757388200993436087 }, 99 | [2]u64{ 11589965406756634890, 1405910560794748869 }, [2]u64{ 7475898206584884855, 2249456897271598191 }, 100 | [2]u64{ 2291369750525997561, 1799565517817278553 }, [2]u64{ 9211793429904618695, 1439652414253822842 }, 101 | [2]u64{ 18428218302589300235, 2303443862806116547 }, [2]u64{ 7363877012587619542, 1842755090244893238 }, 102 | [2]u64{ 13269799239553916280, 1474204072195914590 }, [2]u64{ 10615839391643133024, 1179363257756731672 }, 103 | [2]u64{ 2227947767661371545, 1886981212410770676 }, [2]u64{ 16539753473096738529, 1509584969928616540 }, 104 | [2]u64{ 13231802778477390823, 1207667975942893232 }, [2]u64{ 6413489186596184024, 1932268761508629172 }, 105 | [2]u64{ 16198837793502678189, 1545815009206903337 }, [2]u64{ 5580372605318321905, 1236652007365522670 }, 106 | [2]u64{ 8928596168509315048, 1978643211784836272 }, [2]u64{ 18210923379033183008, 1582914569427869017 }, 107 | [2]u64{ 7190041073742725760, 1266331655542295214 }, [2]u64{ 436019273762630246, 2026130648867672343 }, 108 | [2]u64{ 7727513048493924843, 1620904519094137874 }, [2]u64{ 9871359253537050198, 1296723615275310299 }, 109 | [2]u64{ 4726128361433549347, 2074757784440496479 }, [2]u64{ 7470251503888749801, 1659806227552397183 }, 110 | [2]u64{ 13354898832594820487, 1327844982041917746 }, [2]u64{ 13989140502667892133, 2124551971267068394 }, 111 | [2]u64{ 14880661216876224029, 1699641577013654715 }, [2]u64{ 11904528973500979224, 1359713261610923772 }, 112 | [2]u64{ 4289851098633925465, 2175541218577478036 }, [2]u64{ 18189276137874781665, 1740432974861982428 }, 113 | [2]u64{ 3483374466074094362, 1392346379889585943 }, [2]u64{ 1884050330976640656, 2227754207823337509 }, 114 | [2]u64{ 5196589079523222848, 1782203366258670007 }, [2]u64{ 15225317707844309248, 1425762693006936005 }, 115 | [2]u64{ 5913764258841343181, 2281220308811097609 }, [2]u64{ 8420360221814984868, 1824976247048878087 }, 116 | [2]u64{ 17804334621677718864, 1459980997639102469 }, [2]u64{ 17932816512084085415, 1167984798111281975 }, 117 | [2]u64{ 10245762345624985047, 1868775676978051161 }, [2]u64{ 4507261061758077715, 1495020541582440929 }, 118 | [2]u64{ 7295157664148372495, 1196016433265952743 }, [2]u64{ 7982903447895485668, 1913626293225524389 }, 119 | [2]u64{ 10075671573058298858, 1530901034580419511 }, [2]u64{ 4371188443704728763, 1224720827664335609 }, 120 | [2]u64{ 14372599139411386667, 1959553324262936974 }, [2]u64{ 15187428126271019657, 1567642659410349579 }, 121 | [2]u64{ 15839291315758726049, 1254114127528279663 }, [2]u64{ 3206773216762499739, 2006582604045247462 }, 122 | [2]u64{ 13633465017635730761, 1605266083236197969 }, [2]u64{ 14596120828850494932, 1284212866588958375 }, 123 | [2]u64{ 4907049252451240275, 2054740586542333401 }, [2]u64{ 236290587219081897, 1643792469233866721 }, 124 | [2]u64{ 14946427728742906810, 1315033975387093376 }, [2]u64{ 16535586736504830250, 2104054360619349402 }, 125 | [2]u64{ 5849771759720043554, 1683243488495479522 }, [2]u64{ 15747863852001765813, 1346594790796383617 }, 126 | [2]u64{ 10439186904235184007, 2154551665274213788 }, [2]u64{ 15730047152871967852, 1723641332219371030 }, 127 | [2]u64{ 12584037722297574282, 1378913065775496824 }, [2]u64{ 9066413911450387881, 2206260905240794919 }, 128 | [2]u64{ 10942479943902220628, 1765008724192635935 }, [2]u64{ 8753983955121776503, 1412006979354108748 }, 129 | [2]u64{ 10317025513452932081, 2259211166966573997 }, [2]u64{ 874922781278525018, 1807368933573259198 }, 130 | [2]u64{ 8078635854506640661, 1445895146858607358 }, [2]u64{ 13841606313089133175, 1156716117486885886 }, 131 | [2]u64{ 14767872471458792434, 1850745787979017418 }, [2]u64{ 746251532941302978, 1480596630383213935 }, 132 | [2]u64{ 597001226353042382, 1184477304306571148 }, [2]u64{ 15712597221132509104, 1895163686890513836 }, 133 | [2]u64{ 8880728962164096960, 1516130949512411069 }, [2]u64{ 10793931984473187891, 1212904759609928855 }, 134 | [2]u64{ 17270291175157100626, 1940647615375886168 }, [2]u64{ 2748186495899949531, 1552518092300708935 }, 135 | [2]u64{ 2198549196719959625, 1242014473840567148 }, [2]u64{ 18275073973719576693, 1987223158144907436 }, 136 | [2]u64{ 10930710364233751031, 1589778526515925949 }, [2]u64{ 12433917106128911148, 1271822821212740759 }, 137 | [2]u64{ 8826220925580526867, 2034916513940385215 }, [2]u64{ 7060976740464421494, 1627933211152308172 }, 138 | [2]u64{ 16716827836597268165, 1302346568921846537 }, [2]u64{ 11989529279587987770, 2083754510274954460 }, 139 | [2]u64{ 9591623423670390216, 1667003608219963568 }, [2]u64{ 15051996368420132820, 1333602886575970854 }, 140 | [2]u64{ 13015147745246481542, 2133764618521553367 }, [2]u64{ 3033420566713364587, 1707011694817242694 }, 141 | [2]u64{ 6116085268112601993, 1365609355853794155 }, [2]u64{ 9785736428980163188, 2184974969366070648 }, 142 | [2]u64{ 15207286772667951197, 1747979975492856518 }, [2]u64{ 1097782973908629988, 1398383980394285215 }, 143 | [2]u64{ 1756452758253807981, 2237414368630856344 }, [2]u64{ 5094511021344956708, 1789931494904685075 }, 144 | [2]u64{ 4075608817075965366, 1431945195923748060 }, [2]u64{ 6520974107321544586, 2291112313477996896 }, 145 | [2]u64{ 1527430471115325346, 1832889850782397517 }, [2]u64{ 12289990821117991246, 1466311880625918013 }, 146 | [2]u64{ 17210690286378213644, 1173049504500734410 }, [2]u64{ 9090360384495590213, 1876879207201175057 }, 147 | [2]u64{ 18340334751822203140, 1501503365760940045 }, [2]u64{ 14672267801457762512, 1201202692608752036 }, 148 | [2]u64{ 16096930852848599373, 1921924308174003258 }, [2]u64{ 1809498238053148529, 1537539446539202607 }, 149 | [2]u64{ 12515645034668249793, 1230031557231362085 }, [2]u64{ 1578287981759648052, 1968050491570179337 }, 150 | [2]u64{ 12330676829633449412, 1574440393256143469 }, [2]u64{ 13553890278448669853, 1259552314604914775 }, 151 | [2]u64{ 3239480371808320148, 2015283703367863641 }, [2]u64{ 17348979556414297411, 1612226962694290912 }, 152 | [2]u64{ 6500486015647617283, 1289781570155432730 }, [2]u64{ 10400777625036187652, 2063650512248692368 }, 153 | [2]u64{ 15699319729512770768, 1650920409798953894 }, [2]u64{ 16248804598352126938, 1320736327839163115 }, 154 | [2]u64{ 7551343283653851484, 2113178124542660985 }, [2]u64{ 6041074626923081187, 1690542499634128788 }, 155 | [2]u64{ 12211557331022285596, 1352433999707303030 }, [2]u64{ 1091747655926105338, 2163894399531684849 }, 156 | [2]u64{ 4562746939482794594, 1731115519625347879 }, [2]u64{ 7339546366328145998, 1384892415700278303 }, 157 | [2]u64{ 8053925371383123274, 2215827865120445285 }, [2]u64{ 6443140297106498619, 1772662292096356228 }, 158 | [2]u64{ 12533209867169019542, 1418129833677084982 }, [2]u64{ 5295740528502789974, 2269007733883335972 }, 159 | [2]u64{ 15304638867027962949, 1815206187106668777 }, [2]u64{ 4865013464138549713, 1452164949685335022 }, 160 | [2]u64{ 14960057215536570740, 1161731959748268017 }, [2]u64{ 9178696285890871890, 1858771135597228828 }, 161 | [2]u64{ 14721654658196518159, 1487016908477783062 }, [2]u64{ 4398626097073393881, 1189613526782226450 }, 162 | [2]u64{ 7037801755317430209, 1903381642851562320 }, [2]u64{ 5630241404253944167, 1522705314281249856 }, 163 | [2]u64{ 814844308661245011, 1218164251424999885 }, [2]u64{ 1303750893857992017, 1949062802279999816 }, 164 | [2]u64{ 15800395974054034906, 1559250241823999852 }, [2]u64{ 5261619149759407279, 1247400193459199882 }, 165 | [2]u64{ 12107939454356961969, 1995840309534719811 }, [2]u64{ 5997002748743659252, 1596672247627775849 }, 166 | [2]u64{ 8486951013736837725, 1277337798102220679 }, [2]u64{ 2511075177753209390, 2043740476963553087 }, 167 | [2]u64{ 13076906586428298482, 1634992381570842469 }, [2]u64{ 14150874083884549109, 1307993905256673975 }, 168 | [2]u64{ 4194654460505726958, 2092790248410678361 }, [2]u64{ 18113118827372222859, 1674232198728542688 }, 169 | [2]u64{ 3422448617672047318, 1339385758982834151 }, [2]u64{ 16543964232501006678, 2143017214372534641 }, 170 | [2]u64{ 9545822571258895019, 1714413771498027713 }, [2]u64{ 15015355686490936662, 1371531017198422170 }, 171 | [2]u64{ 5577825024675947042, 2194449627517475473 }, [2]u64{ 11840957649224578280, 1755559702013980378 }, 172 | [2]u64{ 16851463748863483271, 1404447761611184302 }, [2]u64{ 12204946739213931940, 2247116418577894884 }, 173 | [2]u64{ 13453306206113055875, 1797693134862315907 }, [2]u64{ 3383947335406624054, 1438154507889852726 }, 174 | [2]u64{ 16482362180876329456, 2301047212623764361 }, [2]u64{ 9496540929959153242, 1840837770099011489 }, 175 | [2]u64{ 11286581558709232917, 1472670216079209191 }, [2]u64{ 5339916432225476010, 1178136172863367353 }, 176 | [2]u64{ 4854517476818851293, 1885017876581387765 }, [2]u64{ 3883613981455081034, 1508014301265110212 }, 177 | [2]u64{ 14174937629389795797, 1206411441012088169 }, [2]u64{ 11611853762797942306, 1930258305619341071 }, 178 | [2]u64{ 5600134195496443521, 1544206644495472857 }, [2]u64{ 15548153800622885787, 1235365315596378285 }, 179 | [2]u64{ 6430302007287065643, 1976584504954205257 }, [2]u64{ 16212288050055383484, 1581267603963364205 }, 180 | [2]u64{ 12969830440044306787, 1265014083170691364 }, [2]u64{ 9683682259845159889, 2024022533073106183 }, 181 | [2]u64{ 15125643437359948558, 1619218026458484946 }, [2]u64{ 8411165935146048523, 1295374421166787957 }, 182 | [2]u64{ 17147214310975587960, 2072599073866860731 }, [2]u64{ 10028422634038560045, 1658079259093488585 }, 183 | [2]u64{ 8022738107230848036, 1326463407274790868 }, [2]u64{ 9147032156827446534, 2122341451639665389 }, 184 | [2]u64{ 11006974540203867551, 1697873161311732311 }, [2]u64{ 5116230817421183718, 1358298529049385849 }, 185 | [2]u64{ 15564666937357714594, 2173277646479017358 }, [2]u64{ 1383687105660440706, 1738622117183213887 }, 186 | [2]u64{ 12174996128754083534, 1390897693746571109 }, [2]u64{ 8411947361780802685, 2225436309994513775 }, 187 | [2]u64{ 6729557889424642148, 1780349047995611020 }, [2]u64{ 5383646311539713719, 1424279238396488816 }, 188 | [2]u64{ 1235136468979721303, 2278846781434382106 }, [2]u64{ 15745504434151418335, 1823077425147505684 }, 189 | [2]u64{ 16285752362063044992, 1458461940118004547 }, [2]u64{ 5649904260166615347, 1166769552094403638 }, 190 | [2]u64{ 5350498001524674232, 1866831283351045821 }, [2]u64{ 591049586477829062, 1493465026680836657 }, 191 | [2]u64{ 11540886113407994219, 1194772021344669325 }, [2]u64{ 18673707743239135, 1911635234151470921 }, 192 | [2]u64{ 14772334225162232601, 1529308187321176736 }, [2]u64{ 8128518565387875758, 1223446549856941389 }, 193 | [2]u64{ 1937583260394870242, 1957514479771106223 }, [2]u64{ 8928764237799716840, 1566011583816884978 }, 194 | [2]u64{ 14521709019723594119, 1252809267053507982 }, [2]u64{ 8477339172590109297, 2004494827285612772 }, 195 | [2]u64{ 17849917782297818407, 1603595861828490217 }, [2]u64{ 6901236596354434079, 1282876689462792174 }, 196 | [2]u64{ 18420676183650915173, 2052602703140467478 }, [2]u64{ 3668494502695001169, 1642082162512373983 }, 197 | [2]u64{ 10313493231639821582, 1313665730009899186 }, [2]u64{ 9122891541139893884, 2101865168015838698 }, 198 | [2]u64{ 14677010862395735754, 1681492134412670958 }, [2]u64{ 673562245690857633, 1345193707530136767 } 199 | }; 200 | 201 | pub const DOUBLE_POW5_SPLIT = [DOUBLE_POW5_TABLE_SIZE][2]u64 { 202 | [2]u64{ 0, 1152921504606846976 }, [2]u64{ 0, 1441151880758558720 }, 203 | [2]u64{ 0, 1801439850948198400 }, [2]u64{ 0, 2251799813685248000 }, 204 | [2]u64{ 0, 1407374883553280000 }, [2]u64{ 0, 1759218604441600000 }, 205 | [2]u64{ 0, 2199023255552000000 }, [2]u64{ 0, 1374389534720000000 }, 206 | [2]u64{ 0, 1717986918400000000 }, [2]u64{ 0, 2147483648000000000 }, 207 | [2]u64{ 0, 1342177280000000000 }, [2]u64{ 0, 1677721600000000000 }, 208 | [2]u64{ 0, 2097152000000000000 }, [2]u64{ 0, 1310720000000000000 }, 209 | [2]u64{ 0, 1638400000000000000 }, [2]u64{ 0, 2048000000000000000 }, 210 | [2]u64{ 0, 1280000000000000000 }, [2]u64{ 0, 1600000000000000000 }, 211 | [2]u64{ 0, 2000000000000000000 }, [2]u64{ 0, 1250000000000000000 }, 212 | [2]u64{ 0, 1562500000000000000 }, [2]u64{ 0, 1953125000000000000 }, 213 | [2]u64{ 0, 1220703125000000000 }, [2]u64{ 0, 1525878906250000000 }, 214 | [2]u64{ 0, 1907348632812500000 }, [2]u64{ 0, 1192092895507812500 }, 215 | [2]u64{ 0, 1490116119384765625 }, [2]u64{ 4611686018427387904, 1862645149230957031 }, 216 | [2]u64{ 9799832789158199296, 1164153218269348144 }, [2]u64{ 12249790986447749120, 1455191522836685180 }, 217 | [2]u64{ 15312238733059686400, 1818989403545856475 }, [2]u64{ 14528612397897220096, 2273736754432320594 }, 218 | [2]u64{ 13692068767113150464, 1421085471520200371 }, [2]u64{ 12503399940464050176, 1776356839400250464 }, 219 | [2]u64{ 15629249925580062720, 2220446049250313080 }, [2]u64{ 9768281203487539200, 1387778780781445675 }, 220 | [2]u64{ 7598665485932036096, 1734723475976807094 }, [2]u64{ 274959820560269312, 2168404344971008868 }, 221 | [2]u64{ 9395221924704944128, 1355252715606880542 }, [2]u64{ 2520655369026404352, 1694065894508600678 }, 222 | [2]u64{ 12374191248137781248, 2117582368135750847 }, [2]u64{ 14651398557727195136, 1323488980084844279 }, 223 | [2]u64{ 13702562178731606016, 1654361225106055349 }, [2]u64{ 3293144668132343808, 2067951531382569187 }, 224 | [2]u64{ 18199116482078572544, 1292469707114105741 }, [2]u64{ 8913837547316051968, 1615587133892632177 }, 225 | [2]u64{ 15753982952572452864, 2019483917365790221 }, [2]u64{ 12152082354571476992, 1262177448353618888 }, 226 | [2]u64{ 15190102943214346240, 1577721810442023610 }, [2]u64{ 9764256642163156992, 1972152263052529513 }, 227 | [2]u64{ 17631875447420442880, 1232595164407830945 }, [2]u64{ 8204786253993389888, 1540743955509788682 }, 228 | [2]u64{ 1032610780636961552, 1925929944387235853 }, [2]u64{ 2951224747111794922, 1203706215242022408 }, 229 | [2]u64{ 3689030933889743652, 1504632769052528010 }, [2]u64{ 13834660704216955373, 1880790961315660012 }, 230 | [2]u64{ 17870034976990372916, 1175494350822287507 }, [2]u64{ 17725857702810578241, 1469367938527859384 }, 231 | [2]u64{ 3710578054803671186, 1836709923159824231 }, [2]u64{ 26536550077201078, 2295887403949780289 }, 232 | [2]u64{ 11545800389866720434, 1434929627468612680 }, [2]u64{ 14432250487333400542, 1793662034335765850 }, 233 | [2]u64{ 8816941072311974870, 2242077542919707313 }, [2]u64{ 17039803216263454053, 1401298464324817070 }, 234 | [2]u64{ 12076381983474541759, 1751623080406021338 }, [2]u64{ 5872105442488401391, 2189528850507526673 }, 235 | [2]u64{ 15199280947623720629, 1368455531567204170 }, [2]u64{ 9775729147674874978, 1710569414459005213 }, 236 | [2]u64{ 16831347453020981627, 2138211768073756516 }, [2]u64{ 1296220121283337709, 1336382355046097823 }, 237 | [2]u64{ 15455333206886335848, 1670477943807622278 }, [2]u64{ 10095794471753144002, 2088097429759527848 }, 238 | [2]u64{ 6309871544845715001, 1305060893599704905 }, [2]u64{ 12499025449484531656, 1631326116999631131 }, 239 | [2]u64{ 11012095793428276666, 2039157646249538914 }, [2]u64{ 11494245889320060820, 1274473528905961821 }, 240 | [2]u64{ 532749306367912313, 1593091911132452277 }, [2]u64{ 5277622651387278295, 1991364888915565346 }, 241 | [2]u64{ 7910200175544436838, 1244603055572228341 }, [2]u64{ 14499436237857933952, 1555753819465285426 }, 242 | [2]u64{ 8900923260467641632, 1944692274331606783 }, [2]u64{ 12480606065433357876, 1215432671457254239 }, 243 | [2]u64{ 10989071563364309441, 1519290839321567799 }, [2]u64{ 9124653435777998898, 1899113549151959749 }, 244 | [2]u64{ 8008751406574943263, 1186945968219974843 }, [2]u64{ 5399253239791291175, 1483682460274968554 }, 245 | [2]u64{ 15972438586593889776, 1854603075343710692 }, [2]u64{ 759402079766405302, 1159126922089819183 }, 246 | [2]u64{ 14784310654990170340, 1448908652612273978 }, [2]u64{ 9257016281882937117, 1811135815765342473 }, 247 | [2]u64{ 16182956370781059300, 2263919769706678091 }, [2]u64{ 7808504722524468110, 1414949856066673807 }, 248 | [2]u64{ 5148944884728197234, 1768687320083342259 }, [2]u64{ 1824495087482858639, 2210859150104177824 }, 249 | [2]u64{ 1140309429676786649, 1381786968815111140 }, [2]u64{ 1425386787095983311, 1727233711018888925 }, 250 | [2]u64{ 6393419502297367043, 2159042138773611156 }, [2]u64{ 13219259225790630210, 1349401336733506972 }, 251 | [2]u64{ 16524074032238287762, 1686751670916883715 }, [2]u64{ 16043406521870471799, 2108439588646104644 }, 252 | [2]u64{ 803757039314269066, 1317774742903815403 }, [2]u64{ 14839754354425000045, 1647218428629769253 }, 253 | [2]u64{ 4714634887749086344, 2059023035787211567 }, [2]u64{ 9864175832484260821, 1286889397367007229 }, 254 | [2]u64{ 16941905809032713930, 1608611746708759036 }, [2]u64{ 2730638187581340797, 2010764683385948796 }, 255 | [2]u64{ 10930020904093113806, 1256727927116217997 }, [2]u64{ 18274212148543780162, 1570909908895272496 }, 256 | [2]u64{ 4396021111970173586, 1963637386119090621 }, [2]u64{ 5053356204195052443, 1227273366324431638 }, 257 | [2]u64{ 15540067292098591362, 1534091707905539547 }, [2]u64{ 14813398096695851299, 1917614634881924434 }, 258 | [2]u64{ 13870059828862294966, 1198509146801202771 }, [2]u64{ 12725888767650480803, 1498136433501503464 }, 259 | [2]u64{ 15907360959563101004, 1872670541876879330 }, [2]u64{ 14553786618154326031, 1170419088673049581 }, 260 | [2]u64{ 4357175217410743827, 1463023860841311977 }, [2]u64{ 10058155040190817688, 1828779826051639971 }, 261 | [2]u64{ 7961007781811134206, 2285974782564549964 }, [2]u64{ 14199001900486734687, 1428734239102843727 }, 262 | [2]u64{ 13137066357181030455, 1785917798878554659 }, [2]u64{ 11809646928048900164, 2232397248598193324 }, 263 | [2]u64{ 16604401366885338411, 1395248280373870827 }, [2]u64{ 16143815690179285109, 1744060350467338534 }, 264 | [2]u64{ 10956397575869330579, 2180075438084173168 }, [2]u64{ 6847748484918331612, 1362547148802608230 }, 265 | [2]u64{ 17783057643002690323, 1703183936003260287 }, [2]u64{ 17617136035325974999, 2128979920004075359 }, 266 | [2]u64{ 17928239049719816230, 1330612450002547099 }, [2]u64{ 17798612793722382384, 1663265562503183874 }, 267 | [2]u64{ 13024893955298202172, 2079081953128979843 }, [2]u64{ 5834715712847682405, 1299426220705612402 }, 268 | [2]u64{ 16516766677914378815, 1624282775882015502 }, [2]u64{ 11422586310538197711, 2030353469852519378 }, 269 | [2]u64{ 11750802462513761473, 1268970918657824611 }, [2]u64{ 10076817059714813937, 1586213648322280764 }, 270 | [2]u64{ 12596021324643517422, 1982767060402850955 }, [2]u64{ 5566670318688504437, 1239229412751781847 }, 271 | [2]u64{ 2346651879933242642, 1549036765939727309 }, [2]u64{ 7545000868343941206, 1936295957424659136 }, 272 | [2]u64{ 4715625542714963254, 1210184973390411960 }, [2]u64{ 5894531928393704067, 1512731216738014950 }, 273 | [2]u64{ 16591536947346905892, 1890914020922518687 }, [2]u64{ 17287239619732898039, 1181821263076574179 }, 274 | [2]u64{ 16997363506238734644, 1477276578845717724 }, [2]u64{ 2799960309088866689, 1846595723557147156 }, 275 | [2]u64{ 10973347230035317489, 1154122327223216972 }, [2]u64{ 13716684037544146861, 1442652909029021215 }, 276 | [2]u64{ 12534169028502795672, 1803316136286276519 }, [2]u64{ 11056025267201106687, 2254145170357845649 }, 277 | [2]u64{ 18439230838069161439, 1408840731473653530 }, [2]u64{ 13825666510731675991, 1761050914342066913 }, 278 | [2]u64{ 3447025083132431277, 2201313642927583642 }, [2]u64{ 6766076695385157452, 1375821026829739776 }, 279 | [2]u64{ 8457595869231446815, 1719776283537174720 }, [2]u64{ 10571994836539308519, 2149720354421468400 }, 280 | [2]u64{ 6607496772837067824, 1343575221513417750 }, [2]u64{ 17482743002901110588, 1679469026891772187 }, 281 | [2]u64{ 17241742735199000331, 2099336283614715234 }, [2]u64{ 15387775227926763111, 1312085177259197021 }, 282 | [2]u64{ 5399660979626290177, 1640106471573996277 }, [2]u64{ 11361262242960250625, 2050133089467495346 }, 283 | [2]u64{ 11712474920277544544, 1281333180917184591 }, [2]u64{ 10028907631919542777, 1601666476146480739 }, 284 | [2]u64{ 7924448521472040567, 2002083095183100924 }, [2]u64{ 14176152362774801162, 1251301934489438077 }, 285 | [2]u64{ 3885132398186337741, 1564127418111797597 }, [2]u64{ 9468101516160310080, 1955159272639746996 }, 286 | [2]u64{ 15140935484454969608, 1221974545399841872 }, [2]u64{ 479425281859160394, 1527468181749802341 }, 287 | [2]u64{ 5210967620751338397, 1909335227187252926 }, [2]u64{ 17091912818251750210, 1193334516992033078 }, 288 | [2]u64{ 12141518985959911954, 1491668146240041348 }, [2]u64{ 15176898732449889943, 1864585182800051685 }, 289 | [2]u64{ 11791404716994875166, 1165365739250032303 }, [2]u64{ 10127569877816206054, 1456707174062540379 }, 290 | [2]u64{ 8047776328842869663, 1820883967578175474 }, [2]u64{ 836348374198811271, 2276104959472719343 }, 291 | [2]u64{ 7440246761515338900, 1422565599670449589 }, [2]u64{ 13911994470321561530, 1778206999588061986 }, 292 | [2]u64{ 8166621051047176104, 2222758749485077483 }, [2]u64{ 2798295147690791113, 1389224218428173427 }, 293 | [2]u64{ 17332926989895652603, 1736530273035216783 }, [2]u64{ 17054472718942177850, 2170662841294020979 }, 294 | [2]u64{ 8353202440125167204, 1356664275808763112 }, [2]u64{ 10441503050156459005, 1695830344760953890 }, 295 | [2]u64{ 3828506775840797949, 2119787930951192363 }, [2]u64{ 86973725686804766, 1324867456844495227 }, 296 | [2]u64{ 13943775212390669669, 1656084321055619033 }, [2]u64{ 3594660960206173375, 2070105401319523792 }, 297 | [2]u64{ 2246663100128858359, 1293815875824702370 }, [2]u64{ 12031700912015848757, 1617269844780877962 }, 298 | [2]u64{ 5816254103165035138, 2021587305976097453 }, [2]u64{ 5941001823691840913, 1263492066235060908 }, 299 | [2]u64{ 7426252279614801142, 1579365082793826135 }, [2]u64{ 4671129331091113523, 1974206353492282669 }, 300 | [2]u64{ 5225298841145639904, 1233878970932676668 }, [2]u64{ 6531623551432049880, 1542348713665845835 }, 301 | [2]u64{ 3552843420862674446, 1927935892082307294 }, [2]u64{ 16055585193321335241, 1204959932551442058 }, 302 | [2]u64{ 10846109454796893243, 1506199915689302573 }, [2]u64{ 18169322836923504458, 1882749894611628216 }, 303 | [2]u64{ 11355826773077190286, 1176718684132267635 }, [2]u64{ 9583097447919099954, 1470898355165334544 }, 304 | [2]u64{ 11978871809898874942, 1838622943956668180 }, [2]u64{ 14973589762373593678, 2298278679945835225 }, 305 | [2]u64{ 2440964573842414192, 1436424174966147016 }, [2]u64{ 3051205717303017741, 1795530218707683770 }, 306 | [2]u64{ 13037379183483547984, 2244412773384604712 }, [2]u64{ 8148361989677217490, 1402757983365377945 }, 307 | [2]u64{ 14797138505523909766, 1753447479206722431 }, [2]u64{ 13884737113477499304, 2191809349008403039 }, 308 | [2]u64{ 15595489723564518921, 1369880843130251899 }, [2]u64{ 14882676136028260747, 1712351053912814874 }, 309 | [2]u64{ 9379973133180550126, 2140438817391018593 }, [2]u64{ 17391698254306313589, 1337774260869386620 }, 310 | [2]u64{ 3292878744173340370, 1672217826086733276 }, [2]u64{ 4116098430216675462, 2090272282608416595 }, 311 | [2]u64{ 266718509671728212, 1306420176630260372 }, [2]u64{ 333398137089660265, 1633025220787825465 }, 312 | [2]u64{ 5028433689789463235, 2041281525984781831 }, [2]u64{ 10060300083759496378, 1275800953740488644 }, 313 | [2]u64{ 12575375104699370472, 1594751192175610805 }, [2]u64{ 1884160825592049379, 1993438990219513507 }, 314 | [2]u64{ 17318501580490888525, 1245899368887195941 }, [2]u64{ 7813068920331446945, 1557374211108994927 }, 315 | [2]u64{ 5154650131986920777, 1946717763886243659 }, [2]u64{ 915813323278131534, 1216698602428902287 }, 316 | [2]u64{ 14979824709379828129, 1520873253036127858 }, [2]u64{ 9501408849870009354, 1901091566295159823 }, 317 | [2]u64{ 12855909558809837702, 1188182228934474889 }, [2]u64{ 2234828893230133415, 1485227786168093612 }, 318 | [2]u64{ 2793536116537666769, 1856534732710117015 }, [2]u64{ 8663489100477123587, 1160334207943823134 }, 319 | [2]u64{ 1605989338741628675, 1450417759929778918 }, [2]u64{ 11230858710281811652, 1813022199912223647 }, 320 | [2]u64{ 9426887369424876662, 2266277749890279559 }, [2]u64{ 12809333633531629769, 1416423593681424724 }, 321 | [2]u64{ 16011667041914537212, 1770529492101780905 }, [2]u64{ 6179525747111007803, 2213161865127226132 }, 322 | [2]u64{ 13085575628799155685, 1383226165704516332 }, [2]u64{ 16356969535998944606, 1729032707130645415 }, 323 | [2]u64{ 15834525901571292854, 2161290883913306769 }, [2]u64{ 2979049660840976177, 1350806802445816731 }, 324 | [2]u64{ 17558870131333383934, 1688508503057270913 }, [2]u64{ 8113529608884566205, 2110635628821588642 }, 325 | [2]u64{ 9682642023980241782, 1319147268013492901 }, [2]u64{ 16714988548402690132, 1648934085016866126 }, 326 | [2]u64{ 11670363648648586857, 2061167606271082658 }, [2]u64{ 11905663298832754689, 1288229753919426661 }, 327 | [2]u64{ 1047021068258779650, 1610287192399283327 }, [2]u64{ 15143834390605638274, 2012858990499104158 }, 328 | [2]u64{ 4853210475701136017, 1258036869061940099 }, [2]u64{ 1454827076199032118, 1572546086327425124 }, 329 | [2]u64{ 1818533845248790147, 1965682607909281405 }, [2]u64{ 3442426662494187794, 1228551629943300878 }, 330 | [2]u64{ 13526405364972510550, 1535689537429126097 }, [2]u64{ 3072948650933474476, 1919611921786407622 }, 331 | [2]u64{ 15755650962115585259, 1199757451116504763 }, [2]u64{ 15082877684217093670, 1499696813895630954 }, 332 | [2]u64{ 9630225068416591280, 1874621017369538693 }, [2]u64{ 8324733676974063502, 1171638135855961683 }, 333 | [2]u64{ 5794231077790191473, 1464547669819952104 }, [2]u64{ 7242788847237739342, 1830684587274940130 }, 334 | [2]u64{ 18276858095901949986, 2288355734093675162 }, [2]u64{ 16034722328366106645, 1430222333808546976 }, 335 | [2]u64{ 1596658836748081690, 1787777917260683721 }, [2]u64{ 6607509564362490017, 2234722396575854651 }, 336 | [2]u64{ 1823850468512862308, 1396701497859909157 }, [2]u64{ 6891499104068465790, 1745876872324886446 }, 337 | [2]u64{ 17837745916940358045, 2182346090406108057 }, [2]u64{ 4231062170446641922, 1363966306503817536 }, 338 | [2]u64{ 5288827713058302403, 1704957883129771920 }, [2]u64{ 6611034641322878003, 2131197353912214900 }, 339 | [2]u64{ 13355268687681574560, 1331998346195134312 }, [2]u64{ 16694085859601968200, 1664997932743917890 }, 340 | [2]u64{ 11644235287647684442, 2081247415929897363 }, [2]u64{ 4971804045566108824, 1300779634956185852 }, 341 | [2]u64{ 6214755056957636030, 1625974543695232315 }, [2]u64{ 3156757802769657134, 2032468179619040394 }, 342 | [2]u64{ 6584659645158423613, 1270292612261900246 }, [2]u64{ 17454196593302805324, 1587865765327375307 }, 343 | [2]u64{ 17206059723201118751, 1984832206659219134 }, [2]u64{ 6142101308573311315, 1240520129162011959 }, 344 | [2]u64{ 3065940617289251240, 1550650161452514949 }, [2]u64{ 8444111790038951954, 1938312701815643686 }, 345 | [2]u64{ 665883850346957067, 1211445438634777304 }, [2]u64{ 832354812933696334, 1514306798293471630 }, 346 | [2]u64{ 10263815553021896226, 1892883497866839537 }, [2]u64{ 17944099766707154901, 1183052186166774710 }, 347 | [2]u64{ 13206752671529167818, 1478815232708468388 }, [2]u64{ 16508440839411459773, 1848519040885585485 }, 348 | [2]u64{ 12623618533845856310, 1155324400553490928 }, [2]u64{ 15779523167307320387, 1444155500691863660 }, 349 | [2]u64{ 1277659885424598868, 1805194375864829576 }, [2]u64{ 1597074856780748586, 2256492969831036970 }, 350 | [2]u64{ 5609857803915355770, 1410308106144398106 }, [2]u64{ 16235694291748970521, 1762885132680497632 }, 351 | [2]u64{ 1847873790976661535, 2203606415850622041 }, [2]u64{ 12684136165428883219, 1377254009906638775 }, 352 | [2]u64{ 11243484188358716120, 1721567512383298469 }, [2]u64{ 219297180166231438, 2151959390479123087 }, 353 | [2]u64{ 7054589765244976505, 1344974619049451929 }, [2]u64{ 13429923224983608535, 1681218273811814911 }, 354 | [2]u64{ 12175718012802122765, 2101522842264768639 }, [2]u64{ 14527352785642408584, 1313451776415480399 }, 355 | [2]u64{ 13547504963625622826, 1641814720519350499 }, [2]u64{ 12322695186104640628, 2052268400649188124 }, 356 | [2]u64{ 16925056528170176201, 1282667750405742577 }, [2]u64{ 7321262604930556539, 1603334688007178222 }, 357 | [2]u64{ 18374950293017971482, 2004168360008972777 }, [2]u64{ 4566814905495150320, 1252605225005607986 }, 358 | [2]u64{ 14931890668723713708, 1565756531257009982 }, [2]u64{ 9441491299049866327, 1957195664071262478 }, 359 | [2]u64{ 1289246043478778550, 1223247290044539049 }, [2]u64{ 6223243572775861092, 1529059112555673811 }, 360 | [2]u64{ 3167368447542438461, 1911323890694592264 }, [2]u64{ 1979605279714024038, 1194577431684120165 }, 361 | [2]u64{ 7086192618069917952, 1493221789605150206 }, [2]u64{ 18081112809442173248, 1866527237006437757 }, 362 | [2]u64{ 13606538515115052232, 1166579523129023598 }, [2]u64{ 7784801107039039482, 1458224403911279498 }, 363 | [2]u64{ 507629346944023544, 1822780504889099373 }, [2]u64{ 5246222702107417334, 2278475631111374216 }, 364 | [2]u64{ 3278889188817135834, 1424047269444608885 }, [2]u64{ 8710297504448807696, 1780059086805761106 } 365 | }; 366 | 367 | // zig fmt: off 368 | -------------------------------------------------------------------------------- /src/ryu64/test_fixed.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ryu64 = @import("../ryu.zig").ryu64; 3 | 4 | fn ieeeFromParts(sign: bool, exponent: u32, mantissa: u64) f64 { 5 | std.debug.assert(exponent <= 2047); 6 | std.debug.assert(mantissa <= (@as(u64, 1) << 53) - 1); 7 | return @bitCast(f64, ((@as(u64, @boolToInt(sign)) << 63) | (@as(u64, exponent) << 52) | mantissa)); 8 | } 9 | 10 | fn expectFixed(v: f64, precision: u32, expected: []const u8) void { 11 | var buffer: [ryu64.max_buf_size.fixed]u8 = undefined; 12 | const s = ryu64.printFixed(&buffer, v, precision); 13 | std.testing.expectEqualSlices(u8, expected, s); 14 | } 15 | 16 | test "fixed basic" { 17 | expectFixed( 18 | ieeeFromParts(false, 1234, 99999), 19 | 0, 20 | "3291009114715486435425664845573426149758869524108446525879746560", 21 | ); 22 | } 23 | 24 | test "fixed zero" { 25 | expectFixed(0.0, 4, "0.0000"); 26 | expectFixed(0.0, 3, "0.000"); 27 | expectFixed(0.0, 2, "0.00"); 28 | expectFixed(0.0, 1, "0.0"); 29 | expectFixed(0.0, 0, "0"); 30 | } 31 | 32 | test "fixed min/max" { 33 | expectFixed( 34 | ieeeFromParts(false, 0, 1), 35 | 1074, 36 | "0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ++ 37 | "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ++ 38 | "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ++ 39 | "000000000000000000000000000000000000000000000000000000049406564584124654417656879286822137" ++ 40 | "236505980261432476442558568250067550727020875186529983636163599237979656469544571773092665" ++ 41 | "671035593979639877479601078187812630071319031140452784581716784898210368871863605699873072" ++ 42 | "305000638740915356498438731247339727316961514003171538539807412623856559117102665855668676" ++ 43 | "818703956031062493194527159149245532930545654440112748012970999954193198940908041656332452" ++ 44 | "475714786901472678015935523861155013480352649347201937902681071074917033322268447533357208" ++ 45 | "324319360923828934583680601060115061698097530783422773183292479049825247307763759272478746" ++ 46 | "560847782037344696995336470179726777175851256605511991315048911014510378627381672509558373" ++ 47 | "89733598993664809941164205702637090279242767544565229087538682506419718265533447265625", 48 | ); 49 | 50 | expectFixed( 51 | ieeeFromParts(false, 2046, 0xfffffffffffff), 52 | 0, 53 | "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558" ++ 54 | "632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245" ++ 55 | "490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168" ++ 56 | "738177180919299881250404026184124858368", 57 | ); 58 | } 59 | 60 | test "fixed round to even" { 61 | expectFixed(0.125, 3, "0.125"); 62 | expectFixed(0.125, 2, "0.12"); 63 | expectFixed(0.375, 3, "0.375"); 64 | expectFixed(0.375, 2, "0.38"); 65 | } 66 | 67 | test "fixed round to even integer" { 68 | expectFixed(2.5, 1, "2.5"); 69 | expectFixed(2.5, 0, "2"); 70 | expectFixed(3.5, 1, "3.5"); 71 | expectFixed(3.5, 0, "4"); 72 | } 73 | 74 | test "fixed non round to even" { 75 | expectFixed(0.748046875, 3, "0.748"); 76 | expectFixed(0.748046875, 2, "0.75"); 77 | expectFixed(0.748046875, 1, "0.7"); // 0.75 would round to "0.8", but this is smaller 78 | 79 | expectFixed(0.2509765625, 3, "0.251"); 80 | expectFixed(0.2509765625, 2, "0.25"); 81 | expectFixed(0.2509765625, 1, "0.3"); // 0.25 would round to "0.2", but this is larger 82 | 83 | expectFixed(ieeeFromParts(false, 1021, 1), 54, "0.250000000000000055511151231257827021181583404541015625"); 84 | expectFixed(ieeeFromParts(false, 1021, 1), 3, "0.250"); 85 | expectFixed(ieeeFromParts(false, 1021, 1), 2, "0.25"); 86 | expectFixed(ieeeFromParts(false, 1021, 1), 1, "0.3"); // 0.25 would round to "0.2", but this is larger (again) 87 | } 88 | 89 | test "fixed varying precision" { 90 | expectFixed(1729.142857142857, 47, "1729.14285714285711037518922239542007446289062500000"); 91 | expectFixed(1729.142857142857, 46, "1729.1428571428571103751892223954200744628906250000"); 92 | expectFixed(1729.142857142857, 45, "1729.142857142857110375189222395420074462890625000"); 93 | expectFixed(1729.142857142857, 44, "1729.14285714285711037518922239542007446289062500"); 94 | expectFixed(1729.142857142857, 43, "1729.1428571428571103751892223954200744628906250"); 95 | expectFixed(1729.142857142857, 42, "1729.142857142857110375189222395420074462890625"); 96 | expectFixed(1729.142857142857, 41, "1729.14285714285711037518922239542007446289062"); 97 | expectFixed(1729.142857142857, 40, "1729.1428571428571103751892223954200744628906"); 98 | expectFixed(1729.142857142857, 39, "1729.142857142857110375189222395420074462891"); 99 | expectFixed(1729.142857142857, 38, "1729.14285714285711037518922239542007446289"); 100 | expectFixed(1729.142857142857, 37, "1729.1428571428571103751892223954200744629"); 101 | expectFixed(1729.142857142857, 36, "1729.142857142857110375189222395420074463"); 102 | expectFixed(1729.142857142857, 35, "1729.14285714285711037518922239542007446"); 103 | expectFixed(1729.142857142857, 34, "1729.1428571428571103751892223954200745"); 104 | expectFixed(1729.142857142857, 33, "1729.142857142857110375189222395420074"); 105 | expectFixed(1729.142857142857, 32, "1729.14285714285711037518922239542007"); 106 | expectFixed(1729.142857142857, 31, "1729.1428571428571103751892223954201"); 107 | expectFixed(1729.142857142857, 30, "1729.142857142857110375189222395420"); 108 | expectFixed(1729.142857142857, 29, "1729.14285714285711037518922239542"); 109 | expectFixed(1729.142857142857, 28, "1729.1428571428571103751892223954"); 110 | expectFixed(1729.142857142857, 27, "1729.142857142857110375189222395"); 111 | expectFixed(1729.142857142857, 26, "1729.14285714285711037518922240"); 112 | expectFixed(1729.142857142857, 25, "1729.1428571428571103751892224"); 113 | expectFixed(1729.142857142857, 24, "1729.142857142857110375189222"); 114 | expectFixed(1729.142857142857, 23, "1729.14285714285711037518922"); 115 | expectFixed(1729.142857142857, 22, "1729.1428571428571103751892"); 116 | expectFixed(1729.142857142857, 21, "1729.142857142857110375189"); 117 | expectFixed(1729.142857142857, 20, "1729.14285714285711037519"); 118 | expectFixed(1729.142857142857, 19, "1729.1428571428571103752"); 119 | expectFixed(1729.142857142857, 18, "1729.142857142857110375"); 120 | expectFixed(1729.142857142857, 17, "1729.14285714285711038"); 121 | expectFixed(1729.142857142857, 16, "1729.1428571428571104"); 122 | expectFixed(1729.142857142857, 15, "1729.142857142857110"); 123 | expectFixed(1729.142857142857, 14, "1729.14285714285711"); 124 | expectFixed(1729.142857142857, 13, "1729.1428571428571"); 125 | expectFixed(1729.142857142857, 12, "1729.142857142857"); 126 | expectFixed(1729.142857142857, 11, "1729.14285714286"); 127 | expectFixed(1729.142857142857, 10, "1729.1428571429"); 128 | expectFixed(1729.142857142857, 9, "1729.142857143"); 129 | expectFixed(1729.142857142857, 8, "1729.14285714"); 130 | expectFixed(1729.142857142857, 7, "1729.1428571"); 131 | expectFixed(1729.142857142857, 6, "1729.142857"); 132 | expectFixed(1729.142857142857, 5, "1729.14286"); 133 | expectFixed(1729.142857142857, 4, "1729.1429"); 134 | expectFixed(1729.142857142857, 3, "1729.143"); 135 | expectFixed(1729.142857142857, 2, "1729.14"); 136 | expectFixed(1729.142857142857, 1, "1729.1"); 137 | expectFixed(1729.142857142857, 0, "1729"); 138 | } 139 | 140 | test "fixed carrying" { 141 | expectFixed(0.0009, 4, "0.0009"); 142 | expectFixed(0.0009, 3, "0.001"); 143 | expectFixed(0.0029, 4, "0.0029"); 144 | expectFixed(0.0029, 3, "0.003"); 145 | expectFixed(0.0099, 4, "0.0099"); 146 | expectFixed(0.0099, 3, "0.010"); 147 | expectFixed(0.0299, 4, "0.0299"); 148 | expectFixed(0.0299, 3, "0.030"); 149 | expectFixed(0.0999, 4, "0.0999"); 150 | expectFixed(0.0999, 3, "0.100"); 151 | expectFixed(0.2999, 4, "0.2999"); 152 | expectFixed(0.2999, 3, "0.300"); 153 | expectFixed(0.9999, 4, "0.9999"); 154 | expectFixed(0.9999, 3, "1.000"); 155 | expectFixed(2.9999, 4, "2.9999"); 156 | expectFixed(2.9999, 3, "3.000"); 157 | expectFixed(9.9999, 4, "9.9999"); 158 | expectFixed(9.9999, 3, "10.000"); 159 | expectFixed(29.9999, 4, "29.9999"); 160 | expectFixed(29.9999, 3, "30.000"); 161 | expectFixed(99.9999, 4, "99.9999"); 162 | expectFixed(99.9999, 3, "100.000"); 163 | expectFixed(299.9999, 4, "299.9999"); 164 | expectFixed(299.9999, 3, "300.000"); 165 | 166 | expectFixed(0.09, 2, "0.09"); 167 | expectFixed(0.09, 1, "0.1"); 168 | expectFixed(0.29, 2, "0.29"); 169 | expectFixed(0.29, 1, "0.3"); 170 | expectFixed(0.99, 2, "0.99"); 171 | expectFixed(0.99, 1, "1.0"); 172 | expectFixed(2.99, 2, "2.99"); 173 | expectFixed(2.99, 1, "3.0"); 174 | expectFixed(9.99, 2, "9.99"); 175 | expectFixed(9.99, 1, "10.0"); 176 | expectFixed(29.99, 2, "29.99"); 177 | expectFixed(29.99, 1, "30.0"); 178 | expectFixed(99.99, 2, "99.99"); 179 | expectFixed(99.99, 1, "100.0"); 180 | expectFixed(299.99, 2, "299.99"); 181 | expectFixed(299.99, 1, "300.0"); 182 | 183 | expectFixed(0.9, 1, "0.9"); 184 | expectFixed(0.9, 0, "1"); 185 | expectFixed(2.9, 1, "2.9"); 186 | expectFixed(2.9, 0, "3"); 187 | expectFixed(9.9, 1, "9.9"); 188 | expectFixed(9.9, 0, "10"); 189 | expectFixed(29.9, 1, "29.9"); 190 | expectFixed(29.9, 0, "30"); 191 | expectFixed(99.9, 1, "99.9"); 192 | expectFixed(99.9, 0, "100"); 193 | expectFixed(299.9, 1, "299.9"); 194 | expectFixed(299.9, 0, "300"); 195 | } 196 | 197 | test "fixed rounding result zero" { 198 | expectFixed(0.004, 3, "0.004"); 199 | expectFixed(0.004, 2, "0.00"); 200 | expectFixed(0.4, 1, "0.4"); 201 | expectFixed(0.4, 0, "0"); 202 | expectFixed(0.5, 1, "0.5"); 203 | expectFixed(0.5, 0, "0"); 204 | } 205 | 206 | test "fixed regression #1" { 207 | expectFixed(7.018232e-82, 6, "0.000000"); 208 | } 209 | -------------------------------------------------------------------------------- /src/ryu64/test_scientific.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ryu64 = @import("../ryu.zig").ryu64; 3 | 4 | fn ieeeFromParts(sign: bool, exponent: u32, mantissa: u64) f64 { 5 | std.debug.assert(exponent <= 2047); 6 | std.debug.assert(mantissa <= (@as(u64, 1) << 53) - 1); 7 | return @bitCast(f64, ((@as(u64, @boolToInt(sign)) << 63) | (@as(u64, exponent) << 52) | mantissa)); 8 | } 9 | 10 | fn expectExp(v: f64, precision: u32, expected: []const u8) void { 11 | var buffer: [ryu64.max_buf_size.scientific]u8 = undefined; 12 | const s = ryu64.printScientific(&buffer, v, precision); 13 | std.testing.expectEqualSlices(u8, expected, s); 14 | } 15 | 16 | test "basic" { 17 | expectExp( 18 | ieeeFromParts(false, 1234, 99999), 19 | 62, 20 | "3.29100911471548643542566484557342614975886952410844652587974656e+63", 21 | ); 22 | } 23 | 24 | test "zero" { 25 | expectExp(0.0, 4, "0.0000e+00"); 26 | expectExp(0.0, 3, "0.000e+00"); 27 | expectExp(0.0, 2, "0.00e+00"); 28 | expectExp(0.0, 1, "0.0e+00"); 29 | expectExp(0.0, 0, "0e+00"); 30 | } 31 | 32 | test "min/max" { 33 | expectExp( 34 | ieeeFromParts(false, 0, 1), 35 | 750, 36 | "4.9406564584124654417656879286822137236505980261432476442558568250067550727020875186529983" ++ 37 | "636163599237979656469544571773092665671035593979639877479601078187812630071319031140452784" ++ 38 | "581716784898210368871863605699873072305000638740915356498438731247339727316961514003171538" ++ 39 | "539807412623856559117102665855668676818703956031062493194527159149245532930545654440112748" ++ 40 | "012970999954193198940908041656332452475714786901472678015935523861155013480352649347201937" ++ 41 | "902681071074917033322268447533357208324319360923828934583680601060115061698097530783422773" ++ 42 | "183292479049825247307763759272478746560847782037344696995336470179726777175851256605511991" ++ 43 | "315048911014510378627381672509558373897335989936648099411642057026370902792427675445652290" ++ 44 | "87538682506419718265533447265625e-324", 45 | ); 46 | 47 | expectExp( 48 | ieeeFromParts(false, 2046, 0xfffffffffffff), 49 | 308, 50 | "1.7976931348623157081452742373170435679807056752584499659891747680315726078002853876058955" ++ 51 | "863276687817154045895351438246423432132688946418276846754670353751698604991057655128207624" ++ 52 | "549009038932894407586850845513394230458323690322294816580855933212334827479782620414472316" ++ 53 | "8738177180919299881250404026184124858368e+308", 54 | ); 55 | } 56 | 57 | test "round to even" { 58 | expectExp(0.125, 2, "1.25e-01"); 59 | expectExp(0.125, 1, "1.2e-01"); 60 | expectExp(0.375, 2, "3.75e-01"); 61 | expectExp(0.375, 1, "3.8e-01"); 62 | } 63 | 64 | test "round to even integer" { 65 | expectExp(2.5, 1, "2.5e+00"); 66 | expectExp(2.5, 0, "2e+00"); 67 | expectExp(3.5, 1, "3.5e+00"); 68 | expectExp(3.5, 0, "4e+00"); 69 | } 70 | 71 | test "non round to even" { 72 | expectExp(0.748046875, 2, "7.48e-01"); 73 | expectExp(0.748046875, 1, "7.5e-01"); 74 | expectExp(0.748046875, 0, "7e-01"); // 0.75 would round to "8e-01", but this is smaller 75 | 76 | expectExp(0.2509765625, 2, "2.51e-01"); 77 | expectExp(0.2509765625, 1, "2.5e-01"); 78 | expectExp(0.2509765625, 0, "3e-01"); // 0.25 would round to "2e-01", but this is larger 79 | 80 | expectExp(ieeeFromParts(false, 1021, 1), 53, "2.50000000000000055511151231257827021181583404541015625e-01"); 81 | expectExp(ieeeFromParts(false, 1021, 1), 2, "2.50e-01"); 82 | expectExp(ieeeFromParts(false, 1021, 1), 1, "2.5e-01"); 83 | expectExp(ieeeFromParts(false, 1021, 1), 0, "3e-01"); // 0.25 would round to "2e-01", but this is larger (again) 84 | } 85 | 86 | test "varying precision" { 87 | expectExp(1729.142857142857, 50, "1.72914285714285711037518922239542007446289062500000e+03"); 88 | expectExp(1729.142857142857, 49, "1.7291428571428571103751892223954200744628906250000e+03"); 89 | expectExp(1729.142857142857, 48, "1.729142857142857110375189222395420074462890625000e+03"); 90 | expectExp(1729.142857142857, 47, "1.72914285714285711037518922239542007446289062500e+03"); 91 | expectExp(1729.142857142857, 46, "1.7291428571428571103751892223954200744628906250e+03"); 92 | expectExp(1729.142857142857, 45, "1.729142857142857110375189222395420074462890625e+03"); 93 | expectExp(1729.142857142857, 44, "1.72914285714285711037518922239542007446289062e+03"); 94 | expectExp(1729.142857142857, 43, "1.7291428571428571103751892223954200744628906e+03"); 95 | expectExp(1729.142857142857, 42, "1.729142857142857110375189222395420074462891e+03"); 96 | expectExp(1729.142857142857, 41, "1.72914285714285711037518922239542007446289e+03"); 97 | expectExp(1729.142857142857, 40, "1.7291428571428571103751892223954200744629e+03"); 98 | expectExp(1729.142857142857, 39, "1.729142857142857110375189222395420074463e+03"); 99 | expectExp(1729.142857142857, 38, "1.72914285714285711037518922239542007446e+03"); 100 | expectExp(1729.142857142857, 37, "1.7291428571428571103751892223954200745e+03"); 101 | expectExp(1729.142857142857, 36, "1.729142857142857110375189222395420074e+03"); 102 | expectExp(1729.142857142857, 35, "1.72914285714285711037518922239542007e+03"); 103 | expectExp(1729.142857142857, 34, "1.7291428571428571103751892223954201e+03"); 104 | expectExp(1729.142857142857, 33, "1.729142857142857110375189222395420e+03"); 105 | expectExp(1729.142857142857, 32, "1.72914285714285711037518922239542e+03"); 106 | expectExp(1729.142857142857, 31, "1.7291428571428571103751892223954e+03"); 107 | expectExp(1729.142857142857, 30, "1.729142857142857110375189222395e+03"); 108 | expectExp(1729.142857142857, 29, "1.72914285714285711037518922240e+03"); 109 | expectExp(1729.142857142857, 28, "1.7291428571428571103751892224e+03"); 110 | expectExp(1729.142857142857, 27, "1.729142857142857110375189222e+03"); 111 | expectExp(1729.142857142857, 26, "1.72914285714285711037518922e+03"); 112 | expectExp(1729.142857142857, 25, "1.7291428571428571103751892e+03"); 113 | expectExp(1729.142857142857, 24, "1.729142857142857110375189e+03"); 114 | expectExp(1729.142857142857, 23, "1.72914285714285711037519e+03"); 115 | expectExp(1729.142857142857, 22, "1.7291428571428571103752e+03"); 116 | expectExp(1729.142857142857, 21, "1.729142857142857110375e+03"); 117 | expectExp(1729.142857142857, 20, "1.72914285714285711038e+03"); 118 | expectExp(1729.142857142857, 19, "1.7291428571428571104e+03"); 119 | expectExp(1729.142857142857, 18, "1.729142857142857110e+03"); 120 | expectExp(1729.142857142857, 17, "1.72914285714285711e+03"); 121 | expectExp(1729.142857142857, 16, "1.7291428571428571e+03"); 122 | expectExp(1729.142857142857, 15, "1.729142857142857e+03"); 123 | expectExp(1729.142857142857, 14, "1.72914285714286e+03"); 124 | expectExp(1729.142857142857, 13, "1.7291428571429e+03"); 125 | expectExp(1729.142857142857, 12, "1.729142857143e+03"); 126 | expectExp(1729.142857142857, 11, "1.72914285714e+03"); 127 | expectExp(1729.142857142857, 10, "1.7291428571e+03"); 128 | expectExp(1729.142857142857, 9, "1.729142857e+03"); 129 | expectExp(1729.142857142857, 8, "1.72914286e+03"); 130 | expectExp(1729.142857142857, 7, "1.7291429e+03"); 131 | expectExp(1729.142857142857, 6, "1.729143e+03"); 132 | expectExp(1729.142857142857, 5, "1.72914e+03"); 133 | expectExp(1729.142857142857, 4, "1.7291e+03"); 134 | expectExp(1729.142857142857, 3, "1.729e+03"); 135 | expectExp(1729.142857142857, 2, "1.73e+03"); 136 | expectExp(1729.142857142857, 1, "1.7e+03"); 137 | expectExp(1729.142857142857, 0, "2e+03"); 138 | } 139 | 140 | test "carrying" { 141 | expectExp(2.0009, 4, "2.0009e+00"); 142 | expectExp(2.0009, 3, "2.001e+00"); 143 | expectExp(2.0029, 4, "2.0029e+00"); 144 | expectExp(2.0029, 3, "2.003e+00"); 145 | expectExp(2.0099, 4, "2.0099e+00"); 146 | expectExp(2.0099, 3, "2.010e+00"); 147 | expectExp(2.0299, 4, "2.0299e+00"); 148 | expectExp(2.0299, 3, "2.030e+00"); 149 | expectExp(2.0999, 4, "2.0999e+00"); 150 | expectExp(2.0999, 3, "2.100e+00"); 151 | expectExp(2.2999, 4, "2.2999e+00"); 152 | expectExp(2.2999, 3, "2.300e+00"); 153 | expectExp(2.9999, 4, "2.9999e+00"); 154 | expectExp(2.9999, 3, "3.000e+00"); 155 | expectExp(9.9999, 4, "9.9999e+00"); 156 | expectExp(9.9999, 3, "1.000e+01"); 157 | 158 | expectExp(2.09, 2, "2.09e+00"); 159 | expectExp(2.09, 1, "2.1e+00"); 160 | expectExp(2.29, 2, "2.29e+00"); 161 | expectExp(2.29, 1, "2.3e+00"); 162 | expectExp(2.99, 2, "2.99e+00"); 163 | expectExp(2.99, 1, "3.0e+00"); 164 | expectExp(9.99, 2, "9.99e+00"); 165 | expectExp(9.99, 1, "1.0e+01"); 166 | 167 | expectExp(2.9, 1, "2.9e+00"); 168 | expectExp(2.9, 0, "3e+00"); 169 | expectExp(9.9, 1, "9.9e+00"); 170 | expectExp(9.9, 0, "1e+01"); 171 | } 172 | 173 | test "exponents" { 174 | expectExp(9.99e-100, 2, "9.99e-100"); 175 | expectExp(9.99e-99, 2, "9.99e-99"); 176 | expectExp(9.99e-10, 2, "9.99e-10"); 177 | expectExp(9.99e-09, 2, "9.99e-09"); 178 | expectExp(9.99e-01, 2, "9.99e-01"); 179 | expectExp(9.99e+00, 2, "9.99e+00"); 180 | expectExp(9.99e+01, 2, "9.99e+01"); 181 | expectExp(9.99e+09, 2, "9.99e+09"); 182 | expectExp(9.99e+10, 2, "9.99e+10"); 183 | expectExp(9.99e+99, 2, "9.99e+99"); 184 | expectExp(9.99e+100, 2, "9.99e+100"); 185 | 186 | expectExp(9.99e-100, 1, "1.0e-99"); 187 | expectExp(9.99e-99, 1, "1.0e-98"); 188 | expectExp(9.99e-10, 1, "1.0e-09"); 189 | expectExp(9.99e-09, 1, "1.0e-08"); 190 | expectExp(9.99e-01, 1, "1.0e+00"); 191 | expectExp(9.99e+00, 1, "1.0e+01"); 192 | expectExp(9.99e+01, 1, "1.0e+02"); 193 | expectExp(9.99e+09, 1, "1.0e+10"); 194 | expectExp(9.99e+10, 1, "1.0e+11"); 195 | expectExp(9.99e+99, 1, "1.0e+100"); 196 | expectExp(9.99e+100, 1, "1.0e+101"); 197 | } 198 | 199 | test "print decimal point" { 200 | expectExp(1e+54, 0, "1e+54"); 201 | expectExp(1e+54, 1, "1.0e+54"); 202 | expectExp(1e-63, 0, "1e-63"); 203 | expectExp(1e-63, 1, "1.0e-63"); 204 | expectExp(1e+83, 0, "1e+83"); 205 | expectExp(1e+83, 1, "1.0e+83"); 206 | } 207 | 208 | test "all powers of ten" { 209 | for (all_powers_of_ten) |tc| { 210 | expectExp(tc.value, tc.precision, tc.expected); 211 | } 212 | } 213 | 214 | const TestCase = struct { 215 | value: f64, 216 | precision: u32, 217 | expected: []const u8, 218 | 219 | fn new(value: f64, precision: u32, expected: []const u8) TestCase { 220 | return TestCase{ 221 | .value = value, 222 | .precision = precision, 223 | .expected = expected, 224 | }; 225 | } 226 | }; 227 | 228 | const all_powers_of_ten = [_]TestCase{ 229 | TestCase.new( 230 | 1e-323, 231 | 749, 232 | "9.8813129168249308835313758573644274473011960522864952885117136500135101454041750373059967" ++ 233 | "272327198475959312939089143546185331342071187959279754959202156375625260142638062280905569" ++ 234 | "163433569796420737743727211399746144610001277481830712996877462494679454633923028006343077" ++ 235 | "079614825247713118234205331711337353637407912062124986389054318298491065861091308880225496" ++ 236 | "025941999908386397881816083312664904951429573802945356031871047722310026960705298694403875" ++ 237 | "805362142149834066644536895066714416648638721847657869167361202120230123396195061566845546" ++ 238 | "366584958099650494615527518544957493121695564074689393990672940359453554351702513211023982" ++ 239 | "630097822029020757254763345019116747794671979873296198823284114052741805584855350891304581" ++ 240 | "7507736501283943653106689453125e-324", 241 | ), 242 | }; 243 | -------------------------------------------------------------------------------- /src/ryu64/test_shortest.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const ryu64 = @import("../ryu.zig").ryu64; 3 | 4 | fn ieeeFromParts(sign: bool, exponent: u32, mantissa: u64) f64 { 5 | std.debug.assert(exponent <= 2047); 6 | std.debug.assert(mantissa <= (@as(u64, 1) << 53) - 1); 7 | return @bitCast(f64, ((@as(u64, @boolToInt(sign)) << 63) | (@as(u64, exponent) << 52) | mantissa)); 8 | } 9 | 10 | fn testShortest(expected: []const u8, input: f64) void { 11 | var buffer: [ryu64.max_buf_size.shortest]u8 = undefined; 12 | const converted = ryu64.printShortest(buffer[0..], input); 13 | std.debug.assert(std.mem.eql(u8, expected, converted)); 14 | } 15 | 16 | test "basic" { 17 | testShortest("0E0", 0.0); 18 | testShortest("-0E0", -@as(f64, 0.0)); 19 | testShortest("1E0", 1.0); 20 | testShortest("-1E0", -1.0); 21 | testShortest("NaN", std.math.nan(f64)); 22 | testShortest("Infinity", std.math.inf(f64)); 23 | testShortest("-Infinity", -std.math.inf(f64)); 24 | } 25 | 26 | test "switch to subnormal" { 27 | testShortest("2.2250738585072014E-308", 2.2250738585072014E-308); 28 | } 29 | 30 | test "min and max" { 31 | testShortest("1.7976931348623157E308", @bitCast(f64, @as(u64, 0x7fefffffffffffff))); 32 | testShortest("5E-324", @bitCast(f64, @as(u64, 1))); 33 | } 34 | 35 | test "lots of trailing zeros" { 36 | testShortest("2.9802322387695312E-8", 2.98023223876953125E-8); 37 | } 38 | 39 | test "looks like pow5" { 40 | // these numbers have a mantissa that is a multiple of the largest power of 5 that fits, 41 | // and an exponent that causes the computation for q to result in 22, which is a corner 42 | // case for Ryu. 43 | testShortest("5.764607523034235E39", @bitCast(f64, @as(u64, 0x4830F0CF064DD592))); 44 | testShortest("1.152921504606847E40", @bitCast(f64, @as(u64, 0x4840F0CF064DD592))); 45 | testShortest("2.305843009213694E40", @bitCast(f64, @as(u64, 0x4850F0CF064DD592))); 46 | } 47 | 48 | test "output length" { 49 | testShortest("1E0", 1); // already tested in Basic 50 | testShortest("1.2E0", 1.2); 51 | testShortest("1.23E0", 1.23); 52 | testShortest("1.234E0", 1.234); 53 | testShortest("1.2345E0", 1.2345); 54 | testShortest("1.23456E0", 1.23456); 55 | testShortest("1.234567E0", 1.234567); 56 | testShortest("1.2345678E0", 1.2345678); // already tested in Regression 57 | testShortest("1.23456789E0", 1.23456789); 58 | testShortest("1.234567895E0", 1.234567895); // 1.234567890 would be trimmed 59 | testShortest("1.2345678901E0", 1.2345678901); 60 | testShortest("1.23456789012E0", 1.23456789012); 61 | testShortest("1.234567890123E0", 1.234567890123); 62 | testShortest("1.2345678901234E0", 1.2345678901234); 63 | testShortest("1.23456789012345E0", 1.23456789012345); 64 | testShortest("1.234567890123456E0", 1.234567890123456); 65 | testShortest("1.2345678901234567E0", 1.2345678901234567); 66 | 67 | // Test 32-bit chunking 68 | testShortest("4.294967294E0", 4.294967294); // 2^32 - 2 69 | testShortest("4.294967295E0", 4.294967295); // 2^32 - 1 70 | testShortest("4.294967296E0", 4.294967296); // 2^32 71 | testShortest("4.294967297E0", 4.294967297); // 2^32 + 1 72 | testShortest("4.294967298E0", 4.294967298); // 2^32 + 2 73 | } 74 | 75 | test "min/max shift" { 76 | const max_mantissa = (@as(u64, 1) << 53) - 1; 77 | 78 | // 32-bit opt-size=0: 49 <= dist <= 50 79 | // 32-bit opt-size=1: 30 <= dist <= 50 80 | // 64-bit opt-size=0: 50 <= dist <= 50 81 | // 64-bit opt-size=1: 30 <= dist <= 50 82 | testShortest("1.7800590868057611E-307", ieeeFromParts(false, 4, 0)); 83 | // 32-bit opt-size=0: 49 <= dist <= 49 84 | // 32-bit opt-size=1: 28 <= dist <= 49 85 | // 64-bit opt-size=0: 50 <= dist <= 50 86 | // 64-bit opt-size=1: 28 <= dist <= 50 87 | testShortest("2.8480945388892175E-306", ieeeFromParts(false, 6, max_mantissa)); 88 | // 32-bit opt-size=0: 52 <= dist <= 53 89 | // 32-bit opt-size=1: 2 <= dist <= 53 90 | // 64-bit opt-size=0: 53 <= dist <= 53 91 | // 64-bit opt-size=1: 2 <= dist <= 53 92 | testShortest("2.446494580089078E-296", ieeeFromParts(false, 41, 0)); 93 | // 32-bit opt-size=0: 52 <= dist <= 52 94 | // 32-bit opt-size=1: 2 <= dist <= 52 95 | // 64-bit opt-size=0: 53 <= dist <= 53 96 | // 64-bit opt-size=1: 2 <= dist <= 53 97 | testShortest("4.8929891601781557E-296", ieeeFromParts(false, 40, max_mantissa)); 98 | 99 | // 32-bit opt-size=0: 57 <= dist <= 58 100 | // 32-bit opt-size=1: 57 <= dist <= 58 101 | // 64-bit opt-size=0: 58 <= dist <= 58 102 | // 64-bit opt-size=1: 58 <= dist <= 58 103 | testShortest("1.8014398509481984E16", ieeeFromParts(false, 1077, 0)); 104 | // 32-bit opt-size=0: 57 <= dist <= 57 105 | // 32-bit opt-size=1: 57 <= dist <= 57 106 | // 64-bit opt-size=0: 58 <= dist <= 58 107 | // 64-bit opt-size=1: 58 <= dist <= 58 108 | testShortest("3.6028797018963964E16", ieeeFromParts(false, 1076, max_mantissa)); 109 | // 32-bit opt-size=0: 51 <= dist <= 52 110 | // 32-bit opt-size=1: 51 <= dist <= 59 111 | // 64-bit opt-size=0: 52 <= dist <= 52 112 | // 64-bit opt-size=1: 52 <= dist <= 59 113 | testShortest("2.900835519859558E-216", ieeeFromParts(false, 307, 0)); 114 | // 32-bit opt-size=0: 51 <= dist <= 51 115 | // 32-bit opt-size=1: 51 <= dist <= 59 116 | // 64-bit opt-size=0: 52 <= dist <= 52 117 | // 64-bit opt-size=1: 52 <= dist <= 59 118 | testShortest("5.801671039719115E-216", ieeeFromParts(false, 306, max_mantissa)); 119 | 120 | // https://github.com/ulfjack/ryu/commit/19e44d16d80236f5de25800f56d82606d1be00b9#commitcomment-30146483 121 | // 32-bit opt-size=0: 49 <= dist <= 49 122 | // 32-bit opt-size=1: 44 <= dist <= 49 123 | // 64-bit opt-size=0: 50 <= dist <= 50 124 | // 64-bit opt-size=1: 44 <= dist <= 50 125 | testShortest("3.196104012172126E-27", ieeeFromParts(false, 934, 0x000FA7161A4D6E0C)); 126 | } 127 | 128 | test "small integers" { 129 | testShortest("9.007199254740991E15", 9007199254740991.0); // 2^53-1 130 | testShortest("9.007199254740992E15", 9007199254740992.0); // 2^53 131 | 132 | testShortest("1E0", 1.0e+0); 133 | testShortest("1.2E1", 1.2e+1); 134 | testShortest("1.23E2", 1.23e+2); 135 | testShortest("1.234E3", 1.234e+3); 136 | testShortest("1.2345E4", 1.2345e+4); 137 | testShortest("1.23456E5", 1.23456e+5); 138 | testShortest("1.234567E6", 1.234567e+6); 139 | testShortest("1.2345678E7", 1.2345678e+7); 140 | testShortest("1.23456789E8", 1.23456789e+8); 141 | testShortest("1.23456789E9", 1.23456789e+9); 142 | testShortest("1.234567895E9", 1.234567895e+9); 143 | testShortest("1.2345678901E10", 1.2345678901e+10); 144 | testShortest("1.23456789012E11", 1.23456789012e+11); 145 | testShortest("1.234567890123E12", 1.234567890123e+12); 146 | testShortest("1.2345678901234E13", 1.2345678901234e+13); 147 | testShortest("1.23456789012345E14", 1.23456789012345e+14); 148 | testShortest("1.234567890123456E15", 1.234567890123456e+15); 149 | 150 | // 10^i 151 | testShortest("1E0", 1.0e+0); 152 | testShortest("1E1", 1.0e+1); 153 | testShortest("1E2", 1.0e+2); 154 | testShortest("1E3", 1.0e+3); 155 | testShortest("1E4", 1.0e+4); 156 | testShortest("1E5", 1.0e+5); 157 | testShortest("1E6", 1.0e+6); 158 | testShortest("1E7", 1.0e+7); 159 | testShortest("1E8", 1.0e+8); 160 | testShortest("1E9", 1.0e+9); 161 | testShortest("1E10", 1.0e+10); 162 | testShortest("1E11", 1.0e+11); 163 | testShortest("1E12", 1.0e+12); 164 | testShortest("1E13", 1.0e+13); 165 | testShortest("1E14", 1.0e+14); 166 | testShortest("1E15", 1.0e+15); 167 | 168 | // 10^15 + 10^i 169 | testShortest("1.000000000000001E15", 1.0e+15 + 1.0e+0); 170 | testShortest("1.00000000000001E15", 1.0e+15 + 1.0e+1); 171 | testShortest("1.0000000000001E15", 1.0e+15 + 1.0e+2); 172 | testShortest("1.000000000001E15", 1.0e+15 + 1.0e+3); 173 | testShortest("1.00000000001E15", 1.0e+15 + 1.0e+4); 174 | testShortest("1.0000000001E15", 1.0e+15 + 1.0e+5); 175 | testShortest("1.000000001E15", 1.0e+15 + 1.0e+6); 176 | testShortest("1.00000001E15", 1.0e+15 + 1.0e+7); 177 | testShortest("1.0000001E15", 1.0e+15 + 1.0e+8); 178 | testShortest("1.000001E15", 1.0e+15 + 1.0e+9); 179 | testShortest("1.00001E15", 1.0e+15 + 1.0e+10); 180 | testShortest("1.0001E15", 1.0e+15 + 1.0e+11); 181 | testShortest("1.001E15", 1.0e+15 + 1.0e+12); 182 | testShortest("1.01E15", 1.0e+15 + 1.0e+13); 183 | testShortest("1.1E15", 1.0e+15 + 1.0e+14); 184 | 185 | // Largest power of 2 <= 10^(i+1) 186 | testShortest("8E0", 8.0); 187 | testShortest("6.4E1", 64.0); 188 | testShortest("5.12E2", 512.0); 189 | testShortest("8.192E3", 8192.0); 190 | testShortest("6.5536E4", 65536.0); 191 | testShortest("5.24288E5", 524288.0); 192 | testShortest("8.388608E6", 8388608.0); 193 | testShortest("6.7108864E7", 67108864.0); 194 | testShortest("5.36870912E8", 536870912.0); 195 | testShortest("8.589934592E9", 8589934592.0); 196 | testShortest("6.8719476736E10", 68719476736.0); 197 | testShortest("5.49755813888E11", 549755813888.0); 198 | testShortest("8.796093022208E12", 8796093022208.0); 199 | testShortest("7.0368744177664E13", 70368744177664.0); 200 | testShortest("5.62949953421312E14", 562949953421312.0); 201 | testShortest("9.007199254740992E15", 9007199254740992.0); 202 | 203 | // 1000 * (Largest power of 2 <= 10^(i+1)) 204 | testShortest("8E3", 8.0e+3); 205 | testShortest("6.4E4", 64.0e+3); 206 | testShortest("5.12E5", 512.0e+3); 207 | testShortest("8.192E6", 8192.0e+3); 208 | testShortest("6.5536E7", 65536.0e+3); 209 | testShortest("5.24288E8", 524288.0e+3); 210 | testShortest("8.388608E9", 8388608.0e+3); 211 | testShortest("6.7108864E10", 67108864.0e+3); 212 | testShortest("5.36870912E11", 536870912.0e+3); 213 | testShortest("8.589934592E12", 8589934592.0e+3); 214 | testShortest("6.8719476736E13", 68719476736.0e+3); 215 | testShortest("5.49755813888E14", 549755813888.0e+3); 216 | testShortest("8.796093022208E15", 8796093022208.0e+3); 217 | } 218 | 219 | test "ryu64 regression" { 220 | testShortest("-2.109808898695963E16", -2.109808898695963E16); 221 | testShortest("4.940656E-318", 4.940656E-318); 222 | testShortest("1.18575755E-316", 1.18575755E-316); 223 | testShortest("2.989102097996E-312", 2.989102097996E-312); 224 | testShortest("9.0608011534336E15", 9.0608011534336E15); 225 | testShortest("4.708356024711512E18", 4.708356024711512E18); 226 | testShortest("9.409340012568248E18", 9.409340012568248E18); 227 | testShortest("1.2345678E0", 1.2345678); 228 | } 229 | -------------------------------------------------------------------------------- /src/ryu_c.zig: -------------------------------------------------------------------------------- 1 | // C interface for testing. 2 | // 3 | // This matches ryu.h from the reference implementation. 4 | 5 | const std = @import("std"); 6 | const ryu = @import("ryu.zig"); 7 | 8 | var allocator = std.heap.c_allocator; 9 | 10 | export fn d2s_buffered_n(f: f64, result: [*]u8) c_int { 11 | const s = ryu.ryu64.printShortest(result[0..25], f); 12 | return @intCast(c_int, s.len); 13 | } 14 | 15 | export fn d2s_buffered(f: f64, result: [*]u8) void { 16 | const index = d2s_buffered_n(f, result); 17 | result[@intCast(usize, index)] = 0; 18 | } 19 | 20 | export fn d2s(f: f64) ?[*]u8 { 21 | var m = allocator.alloc(u8, 25) catch return null; 22 | const s = ryu.ryu64.printShortest(m, f); 23 | m[s.len] = 0; 24 | return m.ptr; 25 | } 26 | 27 | export fn f2s_buffered_n(f: f32, result: [*]u8) c_int { 28 | const s = ryu.ryu32.printShortest(result[0..25], f); 29 | return @intCast(c_int, s.len); 30 | } 31 | 32 | export fn f2s_buffered(f: f32, result: [*]u8) void { 33 | const index = f2s_buffered_n(f, result); 34 | result[@intCast(usize, index)] = 0; 35 | } 36 | 37 | export fn f2s(f: f32) ?[*]u8 { 38 | var m = allocator.alloc(u8, 25) catch return null; 39 | const s = ryu.ryu32.printShortest(m, f); 40 | m[s.len] = 0; 41 | return m.ptr; 42 | } 43 | 44 | export fn d2fixed_buffered(f: f64, precision: u32, result: [*]u8) void { 45 | const s = ryu.ryu64.printFixed(result[0..2000], f, precision); 46 | result[s.len] = 0; 47 | } 48 | 49 | export fn d2exp_buffered(f: f64, precision: u32, result: [*]u8) void { 50 | const s = ryu.ryu64.printScientific(result[0..2000], f, precision); 51 | result[s.len] = 0; 52 | } 53 | --------------------------------------------------------------------------------