| author | |
| committer | |
| log | 6b41beb370ce62b4381396e0543e6eb7f70b18cf |
| tree | 62ab8274799668635dc2f2fe236f081aea731eef |
| parent | 13392ad33f25649159048757d54fd6d8cf6d1887 |
These conversion routines accept a `round` argument to control how the
result is rounded and return whether the result is exact. Most callers
wanted this functionality and had hacks around it being missing.
Also delete `std.math.big.rational` because it was only being used for
float conversion, and using rationals for that is a lot more complex
than necessary. It also required an allocator, whereas the new integer
routines only need to be passed enough memory to store the result.11 files changed, 779 insertions(+), 997 deletions(-)
lib/compiler/aro/aro/Value.zig+16-26| ... | ... | @@ -148,35 +148,25 @@ pub fn floatToInt(v: *Value, dest_ty: Type, comp: *Compilation) !FloatToIntChang |
| 148 | 148 | return .out_of_range; |
| 149 | 149 | } |
| 150 | 150 | |
| 151 | const had_fraction = @rem(float_val, 1) != 0; | |
| 152 | const is_negative = std.math.signbit(float_val); | |
| 153 | const floored = @floor(@abs(float_val)); | |
| 154 | ||
| 155 | var rational = try std.math.big.Rational.init(comp.gpa); | |
| 156 | defer rational.deinit(); | |
| 157 | rational.setFloat(f128, floored) catch |err| switch (err) { | |
| 158 | error.NonFiniteFloat => { | |
| 159 | v.* = .{}; | |
| 160 | return .overflow; | |
| 161 | }, | |
| 162 | error.OutOfMemory => return error.OutOfMemory, | |
| 163 | }; | |
| 164 | ||
| 165 | // The float is reduced in rational.setFloat, so we assert that denominator is equal to one | |
| 166 | const big_one = BigIntConst{ .limbs = &.{1}, .positive = true }; | |
| 167 | assert(rational.q.toConst().eqlAbs(big_one)); | |
| 168 | ||
| 169 | if (is_negative) { | |
| 170 | rational.negate(); | |
| 171 | } | |
| 172 | ||
| 173 | 151 | const signedness = dest_ty.signedness(comp); |
| 174 | 152 | const bits: usize = @intCast(dest_ty.bitSizeof(comp).?); |
| 175 | 153 | |
| 176 | // rational.p.truncate(rational.p.toConst(), signedness: Signedness, bit_count: usize) | |
| 177 | const fits = rational.p.fitsInTwosComp(signedness, bits); | |
| 178 | v.* = try intern(comp, .{ .int = .{ .big_int = rational.p.toConst() } }); | |
| 179 | try rational.p.truncate(&rational.p, signedness, bits); | |
| 154 | var big_int: std.math.big.int.Mutable = .{ | |
| 155 | .limbs = try comp.gpa.alloc(std.math.big.Limb, @max( | |
| 156 | std.math.big.int.calcLimbLen(float_val), | |
| 157 | std.math.big.int.calcTwosCompLimbCount(bits), | |
| 158 | )), | |
| 159 | .len = undefined, | |
| 160 | .positive = undefined, | |
| 161 | }; | |
| 162 | const had_fraction = switch (big_int.setFloat(float_val, .trunc)) { | |
| 163 | .inexact => true, | |
| 164 | .exact => false, | |
| 165 | }; | |
| 166 | ||
| 167 | const fits = big_int.toConst().fitsInTwosComp(signedness, bits); | |
| 168 | v.* = try intern(comp, .{ .int = .{ .big_int = big_int.toConst() } }); | |
| 169 | big_int.truncate(big_int.toConst(), signedness, bits); | |
| 180 | 170 | |
| 181 | 171 | if (!was_zero and v.isZero(comp)) return .nonzero_to_zero; |
| 182 | 172 | if (!fits) return .out_of_range; |
lib/std/math.zig+1| ... | ... | @@ -45,6 +45,7 @@ pub const rad_per_deg = 0.017453292519943295769236907684886127134428718885417254 |
| 45 | 45 | /// 180.0/pi |
| 46 | 46 | pub const deg_per_rad = 57.295779513082320876798154814105170332405472466564321549160243861; |
| 47 | 47 | |
| 48 | pub const FloatRepr = float.FloatRepr; | |
| 48 | 49 | pub const floatExponentBits = float.floatExponentBits; |
| 49 | 50 | pub const floatMantissaBits = float.floatMantissaBits; |
| 50 | 51 | pub const floatFractionalBits = float.floatFractionalBits; |
lib/std/math/big.zig-2| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | const std = @import("../std.zig"); |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | |
| 4 | pub const Rational = @import("big/rational.zig").Rational; | |
| 5 | 4 | pub const int = @import("big/int.zig"); |
| 6 | 5 | pub const Limb = usize; |
| 7 | 6 | const limb_info = @typeInfo(Limb).int; |
| ... | ... | @@ -18,7 +17,6 @@ comptime { |
| 18 | 17 | |
| 19 | 18 | test { |
| 20 | 19 | _ = int; |
| 21 | _ = Rational; | |
| 22 | 20 | _ = Limb; |
| 23 | 21 | _ = SignedLimb; |
| 24 | 22 | _ = DoubleLimb; |
lib/std/math/big/int.zig+182-46| ... | ... | @@ -18,17 +18,28 @@ const Signedness = std.builtin.Signedness; |
| 18 | 18 | const native_endian = builtin.cpu.arch.endian(); |
| 19 | 19 | |
| 20 | 20 | /// Returns the number of limbs needed to store `scalar`, which must be a |
| 21 | /// primitive integer value. | |
| 21 | /// primitive integer or float value. | |
| 22 | 22 | /// Note: A comptime-known upper bound of this value that may be used |
| 23 | 23 | /// instead if `scalar` is not already comptime-known is |
| 24 | 24 | /// `calcTwosCompLimbCount(@typeInfo(@TypeOf(scalar)).int.bits)` |
| 25 | 25 | pub fn calcLimbLen(scalar: anytype) usize { |
| 26 | if (scalar == 0) { | |
| 27 | return 1; | |
| 26 | switch (@typeInfo(@TypeOf(scalar))) { | |
| 27 | .int, .comptime_int => { | |
| 28 | if (scalar == 0) return 1; | |
| 29 | const w_value = @abs(scalar); | |
| 30 | return @as(usize, @intCast(@divFloor(@as(Limb, @intCast(math.log2(w_value))), limb_bits) + 1)); | |
| 31 | }, | |
| 32 | .float => { | |
| 33 | const repr: std.math.FloatRepr(@TypeOf(scalar)) = @bitCast(scalar); | |
| 34 | return switch (repr.exponent) { | |
| 35 | .denormal => 1, | |
| 36 | else => return calcNonZeroTwosCompLimbCount(@as(usize, 2) + @max(repr.exponent.unbias(), 0)), | |
| 37 | .infinite => 0, | |
| 38 | }; | |
| 39 | }, | |
| 40 | .comptime_float => return calcLimbLen(@as(f128, scalar)), | |
| 41 | else => @compileError("expected float or int, got " ++ @typeName(@TypeOf(scalar))), | |
| 28 | 42 | } |
| 29 | ||
| 30 | const w_value = @abs(scalar); | |
| 31 | return @as(usize, @intCast(@divFloor(@as(Limb, @intCast(math.log2(w_value))), limb_bits) + 1)); | |
| 32 | 43 | } |
| 33 | 44 | |
| 34 | 45 | pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize { |
| ... | ... | @@ -134,6 +145,22 @@ pub const TwosCompIntLimit = enum { |
| 134 | 145 | max, |
| 135 | 146 | }; |
| 136 | 147 | |
| 148 | pub const Round = enum { | |
| 149 | /// Round to the nearest representable value, with ties broken by the representation | |
| 150 | /// that ends with a 0 bit. | |
| 151 | nearest_even, | |
| 152 | /// Round away from zero. | |
| 153 | away, | |
| 154 | /// Round towards zero. | |
| 155 | trunc, | |
| 156 | /// Round towards negative infinity. | |
| 157 | floor, | |
| 158 | /// Round towards positive infinity. | |
| 159 | ceil, | |
| 160 | }; | |
| 161 | ||
| 162 | pub const Exactness = enum { inexact, exact }; | |
| 163 | ||
| 137 | 164 | /// A arbitrary-precision big integer, with a fixed set of mutable limbs. |
| 138 | 165 | pub const Mutable = struct { |
| 139 | 166 | /// Raw digits. These are: |
| ... | ... | @@ -155,6 +182,20 @@ pub const Mutable = struct { |
| 155 | 182 | }; |
| 156 | 183 | } |
| 157 | 184 | |
| 185 | pub const ConvertError = Const.ConvertError; | |
| 186 | ||
| 187 | /// Convert `self` to `Int`. | |
| 188 | /// | |
| 189 | /// Returns an error if self cannot be narrowed into the requested type without truncation. | |
| 190 | pub fn toInt(self: Mutable, comptime Int: type) ConvertError!Int { | |
| 191 | return self.toConst().toInt(Int); | |
| 192 | } | |
| 193 | ||
| 194 | /// Convert `self` to `Float`. | |
| 195 | pub fn toFloat(self: Mutable, comptime Float: type, round: Round) struct { Float, Exactness } { | |
| 196 | return self.toConst().toFloat(Float, round); | |
| 197 | } | |
| 198 | ||
| 158 | 199 | /// Returns true if `a == 0`. |
| 159 | 200 | pub fn eqlZero(self: Mutable) bool { |
| 160 | 201 | return self.toConst().eqlZero(); |
| ... | ... | @@ -401,6 +442,65 @@ pub const Mutable = struct { |
| 401 | 442 | } |
| 402 | 443 | } |
| 403 | 444 | |
| 445 | /// Sets the Mutable to a float value rounded according to `round`. | |
| 446 | /// Returns whether the conversion was exact (`round` had no effect on the result). | |
| 447 | pub fn setFloat(self: *Mutable, value: anytype, round: Round) Exactness { | |
| 448 | const Float = @TypeOf(value); | |
| 449 | if (Float == comptime_float) return self.setFloat(@as(f128, value), round); | |
| 450 | const abs_value = @abs(value); | |
| 451 | if (abs_value < 1.0) { | |
| 452 | if (abs_value == 0.0) { | |
| 453 | self.set(0); | |
| 454 | return .exact; | |
| 455 | } | |
| 456 | self.set(@as(i2, round: switch (round) { | |
| 457 | .nearest_even => if (abs_value <= 0.5) 0 else continue :round .away, | |
| 458 | .away => if (value < 0.0) -1 else 1, | |
| 459 | .trunc => 0, | |
| 460 | .floor => -@as(i2, @intFromBool(value < 0.0)), | |
| 461 | .ceil => @intFromBool(value > 0.0), | |
| 462 | })); | |
| 463 | return .inexact; | |
| 464 | } | |
| 465 | const Repr = std.math.FloatRepr(Float); | |
| 466 | const repr: Repr = @bitCast(value); | |
| 467 | const exponent = repr.exponent.unbias(); | |
| 468 | assert(exponent >= 0); | |
| 469 | const int_bit: Repr.Mantissa = 1 << (@bitSizeOf(Repr.Mantissa) - 1); | |
| 470 | const mantissa = int_bit | repr.mantissa; | |
| 471 | if (exponent >= @bitSizeOf(Repr.Normalized.Fraction)) { | |
| 472 | self.set(mantissa); | |
| 473 | self.shiftLeft(self.toConst(), @intCast(exponent - @bitSizeOf(Repr.Normalized.Fraction))); | |
| 474 | self.positive = repr.sign == .positive; | |
| 475 | return .exact; | |
| 476 | } | |
| 477 | self.set(mantissa >> @intCast(@bitSizeOf(Repr.Normalized.Fraction) - exponent)); | |
| 478 | const round_bits: Repr.Normalized.Fraction = @truncate(mantissa << @intCast(exponent)); | |
| 479 | if (round_bits == 0) { | |
| 480 | self.positive = repr.sign == .positive; | |
| 481 | return .exact; | |
| 482 | } | |
| 483 | round: switch (round) { | |
| 484 | .nearest_even => { | |
| 485 | const half: Repr.Normalized.Fraction = 1 << (@bitSizeOf(Repr.Normalized.Fraction) - 1); | |
| 486 | if (round_bits >= half) self.addScalar(self.toConst(), 1); | |
| 487 | if (round_bits == half) self.limbs[0] &= ~@as(Limb, 1); | |
| 488 | }, | |
| 489 | .away => self.addScalar(self.toConst(), 1), | |
| 490 | .trunc => {}, | |
| 491 | .floor => switch (repr.sign) { | |
| 492 | .positive => {}, | |
| 493 | .negative => continue :round .away, | |
| 494 | }, | |
| 495 | .ceil => switch (repr.sign) { | |
| 496 | .positive => continue :round .away, | |
| 497 | .negative => {}, | |
| 498 | }, | |
| 499 | } | |
| 500 | self.positive = repr.sign == .positive; | |
| 501 | return .inexact; | |
| 502 | } | |
| 503 | ||
| 404 | 504 | /// r = a + scalar |
| 405 | 505 | /// |
| 406 | 506 | /// r and a may be aliases. |
| ... | ... | @@ -2117,25 +2217,25 @@ pub const Const = struct { |
| 2117 | 2217 | /// Deprecated; use `toInt`. |
| 2118 | 2218 | pub const to = toInt; |
| 2119 | 2219 | |
| 2120 | /// Convert self to integer type T. | |
| 2220 | /// Convert `self` to `Int`. | |
| 2121 | 2221 | /// |
| 2122 | 2222 | /// Returns an error if self cannot be narrowed into the requested type without truncation. |
| 2123 | pub fn toInt(self: Const, comptime T: type) ConvertError!T { | |
| 2124 | switch (@typeInfo(T)) { | |
| 2223 | pub fn toInt(self: Const, comptime Int: type) ConvertError!Int { | |
| 2224 | switch (@typeInfo(Int)) { | |
| 2125 | 2225 | .int => |info| { |
| 2126 | 2226 | // Make sure -0 is handled correctly. |
| 2127 | 2227 | if (self.eqlZero()) return 0; |
| 2128 | 2228 | |
| 2129 | const UT = std.meta.Int(.unsigned, info.bits); | |
| 2229 | const Unsigned = std.meta.Int(.unsigned, info.bits); | |
| 2130 | 2230 | |
| 2131 | 2231 | if (!self.fitsInTwosComp(info.signedness, info.bits)) { |
| 2132 | 2232 | return error.TargetTooSmall; |
| 2133 | 2233 | } |
| 2134 | 2234 | |
| 2135 | var r: UT = 0; | |
| 2235 | var r: Unsigned = 0; | |
| 2136 | 2236 | |
| 2137 | if (@sizeOf(UT) <= @sizeOf(Limb)) { | |
| 2138 | r = @as(UT, @intCast(self.limbs[0])); | |
| 2237 | if (@sizeOf(Unsigned) <= @sizeOf(Limb)) { | |
| 2238 | r = @intCast(self.limbs[0]); | |
| 2139 | 2239 | } else { |
| 2140 | 2240 | for (self.limbs[0..self.limbs.len], 0..) |_, ri| { |
| 2141 | 2241 | const limb = self.limbs[self.limbs.len - ri - 1]; |
| ... | ... | @@ -2145,40 +2245,76 @@ pub const Const = struct { |
| 2145 | 2245 | } |
| 2146 | 2246 | |
| 2147 | 2247 | if (info.signedness == .unsigned) { |
| 2148 | return if (self.positive) @as(T, @intCast(r)) else error.NegativeIntoUnsigned; | |
| 2248 | return if (self.positive) @intCast(r) else error.NegativeIntoUnsigned; | |
| 2149 | 2249 | } else { |
| 2150 | 2250 | if (self.positive) { |
| 2151 | 2251 | return @intCast(r); |
| 2152 | 2252 | } else { |
| 2153 | if (math.cast(T, r)) |ok| { | |
| 2253 | if (math.cast(Int, r)) |ok| { | |
| 2154 | 2254 | return -ok; |
| 2155 | 2255 | } else { |
| 2156 | return minInt(T); | |
| 2256 | return minInt(Int); | |
| 2157 | 2257 | } |
| 2158 | 2258 | } |
| 2159 | 2259 | } |
| 2160 | 2260 | }, |
| 2161 | else => @compileError("expected int type, found '" ++ @typeName(T) ++ "'"), | |
| 2261 | else => @compileError("expected int type, found '" ++ @typeName(Int) ++ "'"), | |
| 2162 | 2262 | } |
| 2163 | 2263 | } |
| 2164 | 2264 | |
| 2165 | /// Convert self to float type T. | |
| 2166 | pub fn toFloat(self: Const, comptime T: type) T { | |
| 2167 | if (self.limbs.len == 0) return 0; | |
| 2168 | ||
| 2169 | const base = std.math.maxInt(std.math.big.Limb) + 1; | |
| 2170 | var result: f128 = 0; | |
| 2171 | var i: usize = self.limbs.len; | |
| 2172 | while (i != 0) { | |
| 2173 | i -= 1; | |
| 2174 | const limb: f128 = @floatFromInt(self.limbs[i]); | |
| 2175 | result = @mulAdd(f128, base, result, limb); | |
| 2176 | } | |
| 2177 | if (self.positive) { | |
| 2178 | return @floatCast(result); | |
| 2179 | } else { | |
| 2180 | return @floatCast(-result); | |
| 2181 | } | |
| 2265 | /// Convert self to `Float`. | |
| 2266 | pub fn toFloat(self: Const, comptime Float: type, round: Round) struct { Float, Exactness } { | |
| 2267 | if (Float == comptime_float) return self.toFloat(f128, round); | |
| 2268 | const normalized_abs: Const = .{ | |
| 2269 | .limbs = self.limbs[0..llnormalize(self.limbs)], | |
| 2270 | .positive = true, | |
| 2271 | }; | |
| 2272 | if (normalized_abs.eqlZero()) return .{ if (self.positive) 0.0 else -0.0, .exact }; | |
| 2273 | ||
| 2274 | const Repr = std.math.FloatRepr(Float); | |
| 2275 | var mantissa_limbs: [calcNonZeroTwosCompLimbCount(1 + @bitSizeOf(Repr.Mantissa))]Limb = undefined; | |
| 2276 | var mantissa: Mutable = .{ | |
| 2277 | .limbs = &mantissa_limbs, | |
| 2278 | .positive = undefined, | |
| 2279 | .len = undefined, | |
| 2280 | }; | |
| 2281 | var exponent = normalized_abs.bitCountAbs() - 1; | |
| 2282 | const exactness: Exactness = exactness: { | |
| 2283 | if (exponent <= @bitSizeOf(Repr.Normalized.Fraction)) { | |
| 2284 | mantissa.shiftLeft(normalized_abs, @intCast(@bitSizeOf(Repr.Normalized.Fraction) - exponent)); | |
| 2285 | break :exactness .exact; | |
| 2286 | } | |
| 2287 | const shift: usize = @intCast(exponent - @bitSizeOf(Repr.Normalized.Fraction)); | |
| 2288 | mantissa.shiftRight(normalized_abs, shift); | |
| 2289 | const final_limb_index = (shift - 1) / limb_bits; | |
| 2290 | const round_bits = normalized_abs.limbs[final_limb_index] << @truncate(-%shift) | | |
| 2291 | @intFromBool(!std.mem.allEqual(Limb, normalized_abs.limbs[0..final_limb_index], 0)); | |
| 2292 | if (round_bits == 0) break :exactness .exact; | |
| 2293 | round: switch (round) { | |
| 2294 | .nearest_even => { | |
| 2295 | const half: Limb = 1 << (limb_bits - 1); | |
| 2296 | if (round_bits >= half) mantissa.addScalar(mantissa.toConst(), 1); | |
| 2297 | if (round_bits == half) mantissa.limbs[0] &= ~@as(Limb, 1); | |
| 2298 | }, | |
| 2299 | .away => mantissa.addScalar(mantissa.toConst(), 1), | |
| 2300 | .trunc => {}, | |
| 2301 | .floor => if (!self.positive) continue :round .away, | |
| 2302 | .ceil => if (self.positive) continue :round .away, | |
| 2303 | } | |
| 2304 | break :exactness .inexact; | |
| 2305 | }; | |
| 2306 | const normalized_res: Repr.Normalized = .{ | |
| 2307 | .fraction = @truncate(mantissa.toInt(Repr.Mantissa) catch |err| switch (err) { | |
| 2308 | error.NegativeIntoUnsigned => unreachable, | |
| 2309 | error.TargetTooSmall => fraction: { | |
| 2310 | assert(mantissa.toConst().orderAgainstScalar(1 << @bitSizeOf(Repr.Mantissa)).compare(.eq)); | |
| 2311 | exponent += 1; | |
| 2312 | break :fraction 1 << (@bitSizeOf(Repr.Mantissa) - 1); | |
| 2313 | }, | |
| 2314 | }), | |
| 2315 | .exponent = std.math.lossyCast(Repr.Normalized.Exponent, exponent), | |
| 2316 | }; | |
| 2317 | return .{ normalized_res.reconstruct(if (self.positive) .positive else .negative), exactness }; | |
| 2182 | 2318 | } |
| 2183 | 2319 | |
| 2184 | 2320 | /// To allow `std.fmt.format` to work with this type. |
| ... | ... | @@ -2739,16 +2875,16 @@ pub const Managed = struct { |
| 2739 | 2875 | /// Deprecated; use `toInt`. |
| 2740 | 2876 | pub const to = toInt; |
| 2741 | 2877 | |
| 2742 | /// Convert self to integer type T. | |
| 2878 | /// Convert `self` to `Int`. | |
| 2743 | 2879 | /// |
| 2744 | 2880 | /// Returns an error if self cannot be narrowed into the requested type without truncation. |
| 2745 | pub fn toInt(self: Managed, comptime T: type) ConvertError!T { | |
| 2746 | return self.toConst().toInt(T); | |
| 2881 | pub fn toInt(self: Managed, comptime Int: type) ConvertError!Int { | |
| 2882 | return self.toConst().toInt(Int); | |
| 2747 | 2883 | } |
| 2748 | 2884 | |
| 2749 | /// Convert self to float type T. | |
| 2750 | pub fn toFloat(self: Managed, comptime T: type) T { | |
| 2751 | return self.toConst().toFloat(T); | |
| 2885 | /// Convert `self` to `Float`. | |
| 2886 | pub fn toFloat(self: Managed, comptime Float: type, round: Round) struct { Float, Exactness } { | |
| 2887 | return self.toConst().toFloat(Float, round); | |
| 2752 | 2888 | } |
| 2753 | 2889 | |
| 2754 | 2890 | /// Set self from the string representation `value`. |
| ... | ... | @@ -3807,7 +3943,7 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) usize { |
| 3807 | 3943 | |
| 3808 | 3944 | // if the most significant limb becomes 0 after the shift |
| 3809 | 3945 | const shrink = a[a.len - 1] >> bit_shift == 0; |
| 3810 | std.debug.assert(r.len >= a.len - @intFromBool(!shrink)); | |
| 3946 | std.debug.assert(r.len >= a.len - @intFromBool(shrink)); | |
| 3811 | 3947 | |
| 3812 | 3948 | var i: usize = 0; |
| 3813 | 3949 | while (i < a.len - 1) : (i += 1) { |
| ... | ... | @@ -4240,7 +4376,7 @@ test { |
| 4240 | 4376 | |
| 4241 | 4377 | const testing_allocator = std.testing.allocator; |
| 4242 | 4378 | test "llshl shift by whole number of limb" { |
| 4243 | const padding = std.math.maxInt(Limb); | |
| 4379 | const padding = maxInt(Limb); | |
| 4244 | 4380 | |
| 4245 | 4381 | var r: [10]Limb = @splat(padding); |
| 4246 | 4382 | |
| ... | ... | @@ -4390,8 +4526,8 @@ test "llshr to 0" { |
| 4390 | 4526 | try testOneShiftCase(.llshr, .{1, &.{0}, &.{1}}); |
| 4391 | 4527 | try testOneShiftCase(.llshr, .{5, &.{0}, &.{1}}); |
| 4392 | 4528 | try testOneShiftCase(.llshr, .{65, &.{0}, &.{0, 1}}); |
| 4393 | try testOneShiftCase(.llshr, .{193, &.{0}, &.{0, 0, std.math.maxInt(Limb)}}); | |
| 4394 | try testOneShiftCase(.llshr, .{193, &.{0}, &.{std.math.maxInt(Limb), 1, std.math.maxInt(Limb)}}); | |
| 4529 | try testOneShiftCase(.llshr, .{193, &.{0}, &.{0, 0, maxInt(Limb)}}); | |
| 4530 | try testOneShiftCase(.llshr, .{193, &.{0}, &.{maxInt(Limb), 1, maxInt(Limb)}}); | |
| 4395 | 4531 | try testOneShiftCase(.llshr, .{193, &.{0}, &.{0xdeadbeef, 0xabcdefab, 0x1234}}); |
| 4396 | 4532 | // zig fmt: on |
| 4397 | 4533 | } |
| ... | ... | @@ -4475,7 +4611,7 @@ fn testOneShiftCase(comptime function: enum { llshr, llshl }, case: Case) !void |
| 4475 | 4611 | } |
| 4476 | 4612 | |
| 4477 | 4613 | fn testOneShiftCaseNoAliasing(func: fn ([]Limb, []const Limb, usize) usize, case: Case) !void { |
| 4478 | const padding = std.math.maxInt(Limb); | |
| 4614 | const padding = maxInt(Limb); | |
| 4479 | 4615 | var r: [20]Limb = @splat(padding); |
| 4480 | 4616 | |
| 4481 | 4617 | const shift = case[0]; |
| ... | ... | @@ -4492,7 +4628,7 @@ fn testOneShiftCaseNoAliasing(func: fn ([]Limb, []const Limb, usize) usize, case |
| 4492 | 4628 | } |
| 4493 | 4629 | |
| 4494 | 4630 | fn testOneShiftCaseAliasing(func: fn ([]Limb, []const Limb, usize) usize, case: Case, shift_direction: isize) !void { |
| 4495 | const padding = std.math.maxInt(Limb); | |
| 4631 | const padding = maxInt(Limb); | |
| 4496 | 4632 | var r: [60]Limb = @splat(padding); |
| 4497 | 4633 | const base = 20; |
| 4498 | 4634 |
lib/std/math/big/int_test.zig+407| ... | ... | @@ -17,6 +17,12 @@ const minInt = std.math.minInt; |
| 17 | 17 | // They will still run on larger than this and should pass, but the multi-limb code-paths |
| 18 | 18 | // may be untested in some cases. |
| 19 | 19 | |
| 20 | fn expectNormalized(expected: comptime_int, actual: std.math.big.int.Const) !void { | |
| 21 | try testing.expectEqual(expected >= 0, actual.positive); | |
| 22 | try testing.expectEqual(std.math.big.int.calcLimbLen(expected), actual.limbs.len); | |
| 23 | try testing.expect(actual.orderAgainstScalar(expected).compare(.eq)); | |
| 24 | } | |
| 25 | ||
| 20 | 26 | test "comptime_int set" { |
| 21 | 27 | comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab; |
| 22 | 28 | var a = try Managed.initSet(testing.allocator, s); |
| ... | ... | @@ -85,6 +91,407 @@ test "to target too small error" { |
| 85 | 91 | try testing.expectError(error.TargetTooSmall, a.toInt(u8)); |
| 86 | 92 | } |
| 87 | 93 | |
| 94 | fn setFloat(comptime Float: type) !void { | |
| 95 | var res_limbs: [std.math.big.int.calcNonZeroTwosCompLimbCount(11)]Limb = undefined; | |
| 96 | var res: Mutable = .{ | |
| 97 | .limbs = &res_limbs, | |
| 98 | .len = undefined, | |
| 99 | .positive = undefined, | |
| 100 | }; | |
| 101 | ||
| 102 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0x1p10), .nearest_even)); | |
| 103 | try expectNormalized(-1 << 10, res.toConst()); | |
| 104 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0x1p10), .away)); | |
| 105 | try expectNormalized(-1 << 10, res.toConst()); | |
| 106 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0x1p10), .trunc)); | |
| 107 | try expectNormalized(-1 << 10, res.toConst()); | |
| 108 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0x1p10), .floor)); | |
| 109 | try expectNormalized(-1 << 10, res.toConst()); | |
| 110 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0x1p10), .ceil)); | |
| 111 | try expectNormalized(-1 << 10, res.toConst()); | |
| 112 | ||
| 113 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -2.0), .nearest_even)); | |
| 114 | try expectNormalized(-2, res.toConst()); | |
| 115 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -2.0), .away)); | |
| 116 | try expectNormalized(-2, res.toConst()); | |
| 117 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -2.0), .trunc)); | |
| 118 | try expectNormalized(-2, res.toConst()); | |
| 119 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -2.0), .floor)); | |
| 120 | try expectNormalized(-2, res.toConst()); | |
| 121 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -2.0), .ceil)); | |
| 122 | try expectNormalized(-2, res.toConst()); | |
| 123 | ||
| 124 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -1.5), .nearest_even)); | |
| 125 | try expectNormalized(-2, res.toConst()); | |
| 126 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -1.5), .away)); | |
| 127 | try expectNormalized(-2, res.toConst()); | |
| 128 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -1.5), .trunc)); | |
| 129 | try expectNormalized(-1, res.toConst()); | |
| 130 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -1.5), .floor)); | |
| 131 | try expectNormalized(-2, res.toConst()); | |
| 132 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -1.5), .ceil)); | |
| 133 | try expectNormalized(-1, res.toConst()); | |
| 134 | ||
| 135 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -1.0), .nearest_even)); | |
| 136 | try expectNormalized(-1, res.toConst()); | |
| 137 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -1.0), .away)); | |
| 138 | try expectNormalized(-1, res.toConst()); | |
| 139 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -1.0), .trunc)); | |
| 140 | try expectNormalized(-1, res.toConst()); | |
| 141 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -1.0), .floor)); | |
| 142 | try expectNormalized(-1, res.toConst()); | |
| 143 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -1.0), .ceil)); | |
| 144 | try expectNormalized(-1, res.toConst()); | |
| 145 | ||
| 146 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.75), .nearest_even)); | |
| 147 | try expectNormalized(-1, res.toConst()); | |
| 148 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.75), .away)); | |
| 149 | try expectNormalized(-1, res.toConst()); | |
| 150 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.75), .trunc)); | |
| 151 | try expectNormalized(0, res.toConst()); | |
| 152 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.75), .floor)); | |
| 153 | try expectNormalized(-1, res.toConst()); | |
| 154 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.75), .ceil)); | |
| 155 | try expectNormalized(0, res.toConst()); | |
| 156 | ||
| 157 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.5), .nearest_even)); | |
| 158 | try expectNormalized(0, res.toConst()); | |
| 159 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.5), .away)); | |
| 160 | try expectNormalized(-1, res.toConst()); | |
| 161 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.5), .trunc)); | |
| 162 | try expectNormalized(0, res.toConst()); | |
| 163 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.5), .floor)); | |
| 164 | try expectNormalized(-1, res.toConst()); | |
| 165 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.5), .ceil)); | |
| 166 | try expectNormalized(0, res.toConst()); | |
| 167 | ||
| 168 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.25), .nearest_even)); | |
| 169 | try expectNormalized(0, res.toConst()); | |
| 170 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.25), .away)); | |
| 171 | try expectNormalized(-1, res.toConst()); | |
| 172 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.25), .trunc)); | |
| 173 | try expectNormalized(0, res.toConst()); | |
| 174 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.25), .floor)); | |
| 175 | try expectNormalized(-1, res.toConst()); | |
| 176 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, -0.25), .ceil)); | |
| 177 | try expectNormalized(0, res.toConst()); | |
| 178 | ||
| 179 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0.0), .nearest_even)); | |
| 180 | try expectNormalized(0, res.toConst()); | |
| 181 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0.0), .away)); | |
| 182 | try expectNormalized(0, res.toConst()); | |
| 183 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0.0), .trunc)); | |
| 184 | try expectNormalized(0, res.toConst()); | |
| 185 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0.0), .floor)); | |
| 186 | try expectNormalized(0, res.toConst()); | |
| 187 | try testing.expectEqual(.exact, res.setFloat(@as(Float, -0.0), .ceil)); | |
| 188 | try expectNormalized(0, res.toConst()); | |
| 189 | ||
| 190 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0.0), .nearest_even)); | |
| 191 | try expectNormalized(0, res.toConst()); | |
| 192 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0.0), .away)); | |
| 193 | try expectNormalized(0, res.toConst()); | |
| 194 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0.0), .trunc)); | |
| 195 | try expectNormalized(0, res.toConst()); | |
| 196 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0.0), .floor)); | |
| 197 | try expectNormalized(0, res.toConst()); | |
| 198 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0.0), .ceil)); | |
| 199 | try expectNormalized(0, res.toConst()); | |
| 200 | ||
| 201 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.25), .nearest_even)); | |
| 202 | try expectNormalized(0, res.toConst()); | |
| 203 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.25), .away)); | |
| 204 | try expectNormalized(1, res.toConst()); | |
| 205 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.25), .trunc)); | |
| 206 | try expectNormalized(0, res.toConst()); | |
| 207 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.25), .floor)); | |
| 208 | try expectNormalized(0, res.toConst()); | |
| 209 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.25), .ceil)); | |
| 210 | try expectNormalized(1, res.toConst()); | |
| 211 | ||
| 212 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.5), .nearest_even)); | |
| 213 | try expectNormalized(0, res.toConst()); | |
| 214 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.5), .away)); | |
| 215 | try expectNormalized(1, res.toConst()); | |
| 216 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.5), .trunc)); | |
| 217 | try expectNormalized(0, res.toConst()); | |
| 218 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.5), .floor)); | |
| 219 | try expectNormalized(0, res.toConst()); | |
| 220 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.5), .ceil)); | |
| 221 | try expectNormalized(1, res.toConst()); | |
| 222 | ||
| 223 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.75), .nearest_even)); | |
| 224 | try expectNormalized(1, res.toConst()); | |
| 225 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.75), .away)); | |
| 226 | try expectNormalized(1, res.toConst()); | |
| 227 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.75), .trunc)); | |
| 228 | try expectNormalized(0, res.toConst()); | |
| 229 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.75), .floor)); | |
| 230 | try expectNormalized(0, res.toConst()); | |
| 231 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 0.75), .ceil)); | |
| 232 | try expectNormalized(1, res.toConst()); | |
| 233 | ||
| 234 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 1.0), .nearest_even)); | |
| 235 | try expectNormalized(1, res.toConst()); | |
| 236 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 1.0), .away)); | |
| 237 | try expectNormalized(1, res.toConst()); | |
| 238 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 1.0), .trunc)); | |
| 239 | try expectNormalized(1, res.toConst()); | |
| 240 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 1.0), .floor)); | |
| 241 | try expectNormalized(1, res.toConst()); | |
| 242 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 1.0), .ceil)); | |
| 243 | try expectNormalized(1, res.toConst()); | |
| 244 | ||
| 245 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 1.5), .nearest_even)); | |
| 246 | try expectNormalized(2, res.toConst()); | |
| 247 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 1.5), .away)); | |
| 248 | try expectNormalized(2, res.toConst()); | |
| 249 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 1.5), .trunc)); | |
| 250 | try expectNormalized(1, res.toConst()); | |
| 251 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 1.5), .floor)); | |
| 252 | try expectNormalized(1, res.toConst()); | |
| 253 | try testing.expectEqual(.inexact, res.setFloat(@as(Float, 1.5), .ceil)); | |
| 254 | try expectNormalized(2, res.toConst()); | |
| 255 | ||
| 256 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 2.0), .nearest_even)); | |
| 257 | try expectNormalized(2, res.toConst()); | |
| 258 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 2.0), .away)); | |
| 259 | try expectNormalized(2, res.toConst()); | |
| 260 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 2.0), .trunc)); | |
| 261 | try expectNormalized(2, res.toConst()); | |
| 262 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 2.0), .floor)); | |
| 263 | try expectNormalized(2, res.toConst()); | |
| 264 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 2.0), .ceil)); | |
| 265 | try expectNormalized(2, res.toConst()); | |
| 266 | ||
| 267 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0x1p10), .nearest_even)); | |
| 268 | try expectNormalized(1 << 10, res.toConst()); | |
| 269 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0x1p10), .away)); | |
| 270 | try expectNormalized(1 << 10, res.toConst()); | |
| 271 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0x1p10), .trunc)); | |
| 272 | try expectNormalized(1 << 10, res.toConst()); | |
| 273 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0x1p10), .floor)); | |
| 274 | try expectNormalized(1 << 10, res.toConst()); | |
| 275 | try testing.expectEqual(.exact, res.setFloat(@as(Float, 0x1p10), .ceil)); | |
| 276 | try expectNormalized(1 << 10, res.toConst()); | |
| 277 | } | |
| 278 | test setFloat { | |
| 279 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | |
| 280 | ||
| 281 | try setFloat(f16); | |
| 282 | try setFloat(f32); | |
| 283 | try setFloat(f64); | |
| 284 | try setFloat(f80); | |
| 285 | try setFloat(f128); | |
| 286 | try setFloat(c_longdouble); | |
| 287 | try setFloat(comptime_float); | |
| 288 | } | |
| 289 | ||
| 290 | fn toFloat(comptime Float: type) !void { | |
| 291 | const Result = struct { Float, std.math.big.int.Exactness }; | |
| 292 | const fractional_bits = std.math.floatFractionalBits(Float); | |
| 293 | ||
| 294 | var int_limbs: [ | |
| 295 | std.math.big.int.calcNonZeroTwosCompLimbCount(2 + fractional_bits) | |
| 296 | ]Limb = undefined; | |
| 297 | var int: Mutable = .{ | |
| 298 | .limbs = &int_limbs, | |
| 299 | .len = undefined, | |
| 300 | .positive = undefined, | |
| 301 | }; | |
| 302 | ||
| 303 | int.set(-(1 << (fractional_bits + 1)) - 1); | |
| 304 | try testing.expectEqual( | |
| 305 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1), .inexact }, | |
| 306 | int.toFloat(Float, .nearest_even), | |
| 307 | ); | |
| 308 | try testing.expectEqual( | |
| 309 | Result{ comptime std.math.nextAfter( | |
| 310 | Float, | |
| 311 | -std.math.ldexp(@as(Float, 1), fractional_bits + 1), | |
| 312 | -std.math.inf(Float), | |
| 313 | ), .inexact }, | |
| 314 | int.toFloat(Float, .away), | |
| 315 | ); | |
| 316 | try testing.expectEqual( | |
| 317 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1), .inexact }, | |
| 318 | int.toFloat(Float, .trunc), | |
| 319 | ); | |
| 320 | try testing.expectEqual( | |
| 321 | Result{ comptime std.math.nextAfter( | |
| 322 | Float, | |
| 323 | -std.math.ldexp(@as(Float, 1), fractional_bits + 1), | |
| 324 | -std.math.inf(Float), | |
| 325 | ), .inexact }, | |
| 326 | int.toFloat(Float, .floor), | |
| 327 | ); | |
| 328 | try testing.expectEqual( | |
| 329 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1), .inexact }, | |
| 330 | int.toFloat(Float, .ceil), | |
| 331 | ); | |
| 332 | ||
| 333 | int.set(-1 << (fractional_bits + 1)); | |
| 334 | try testing.expectEqual( | |
| 335 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 336 | int.toFloat(Float, .nearest_even), | |
| 337 | ); | |
| 338 | try testing.expectEqual( | |
| 339 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 340 | int.toFloat(Float, .away), | |
| 341 | ); | |
| 342 | try testing.expectEqual( | |
| 343 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 344 | int.toFloat(Float, .trunc), | |
| 345 | ); | |
| 346 | try testing.expectEqual( | |
| 347 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 348 | int.toFloat(Float, .floor), | |
| 349 | ); | |
| 350 | try testing.expectEqual( | |
| 351 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 352 | int.toFloat(Float, .ceil), | |
| 353 | ); | |
| 354 | ||
| 355 | int.set(-(1 << (fractional_bits + 1)) + 1); | |
| 356 | try testing.expectEqual( | |
| 357 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1) + 1.0, .exact }, | |
| 358 | int.toFloat(Float, .nearest_even), | |
| 359 | ); | |
| 360 | try testing.expectEqual( | |
| 361 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1) + 1.0, .exact }, | |
| 362 | int.toFloat(Float, .away), | |
| 363 | ); | |
| 364 | try testing.expectEqual( | |
| 365 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1) + 1.0, .exact }, | |
| 366 | int.toFloat(Float, .trunc), | |
| 367 | ); | |
| 368 | try testing.expectEqual( | |
| 369 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1) + 1.0, .exact }, | |
| 370 | int.toFloat(Float, .floor), | |
| 371 | ); | |
| 372 | try testing.expectEqual( | |
| 373 | Result{ comptime -std.math.ldexp(@as(Float, 1), fractional_bits + 1) + 1.0, .exact }, | |
| 374 | int.toFloat(Float, .ceil), | |
| 375 | ); | |
| 376 | ||
| 377 | int.set(-1 << 10); | |
| 378 | try testing.expectEqual(Result{ -0x1p10, .exact }, int.toFloat(Float, .nearest_even)); | |
| 379 | try testing.expectEqual(Result{ -0x1p10, .exact }, int.toFloat(Float, .away)); | |
| 380 | try testing.expectEqual(Result{ -0x1p10, .exact }, int.toFloat(Float, .trunc)); | |
| 381 | try testing.expectEqual(Result{ -0x1p10, .exact }, int.toFloat(Float, .floor)); | |
| 382 | try testing.expectEqual(Result{ -0x1p10, .exact }, int.toFloat(Float, .ceil)); | |
| 383 | ||
| 384 | int.set(-1); | |
| 385 | try testing.expectEqual(Result{ -1.0, .exact }, int.toFloat(Float, .nearest_even)); | |
| 386 | try testing.expectEqual(Result{ -1.0, .exact }, int.toFloat(Float, .away)); | |
| 387 | try testing.expectEqual(Result{ -1.0, .exact }, int.toFloat(Float, .trunc)); | |
| 388 | try testing.expectEqual(Result{ -1.0, .exact }, int.toFloat(Float, .floor)); | |
| 389 | try testing.expectEqual(Result{ -1.0, .exact }, int.toFloat(Float, .ceil)); | |
| 390 | ||
| 391 | int.set(0); | |
| 392 | try testing.expectEqual(Result{ 0.0, .exact }, int.toFloat(Float, .nearest_even)); | |
| 393 | try testing.expectEqual(Result{ 0.0, .exact }, int.toFloat(Float, .away)); | |
| 394 | try testing.expectEqual(Result{ 0.0, .exact }, int.toFloat(Float, .trunc)); | |
| 395 | try testing.expectEqual(Result{ 0.0, .exact }, int.toFloat(Float, .floor)); | |
| 396 | try testing.expectEqual(Result{ 0.0, .exact }, int.toFloat(Float, .ceil)); | |
| 397 | ||
| 398 | int.set(1); | |
| 399 | try testing.expectEqual(Result{ 1.0, .exact }, int.toFloat(Float, .nearest_even)); | |
| 400 | try testing.expectEqual(Result{ 1.0, .exact }, int.toFloat(Float, .away)); | |
| 401 | try testing.expectEqual(Result{ 1.0, .exact }, int.toFloat(Float, .trunc)); | |
| 402 | try testing.expectEqual(Result{ 1.0, .exact }, int.toFloat(Float, .floor)); | |
| 403 | try testing.expectEqual(Result{ 1.0, .exact }, int.toFloat(Float, .ceil)); | |
| 404 | ||
| 405 | int.set(1 << 10); | |
| 406 | try testing.expectEqual(Result{ 0x1p10, .exact }, int.toFloat(Float, .nearest_even)); | |
| 407 | try testing.expectEqual(Result{ 0x1p10, .exact }, int.toFloat(Float, .away)); | |
| 408 | try testing.expectEqual(Result{ 0x1p10, .exact }, int.toFloat(Float, .trunc)); | |
| 409 | try testing.expectEqual(Result{ 0x1p10, .exact }, int.toFloat(Float, .floor)); | |
| 410 | try testing.expectEqual(Result{ 0x1p10, .exact }, int.toFloat(Float, .ceil)); | |
| 411 | ||
| 412 | int.set((1 << (fractional_bits + 1)) - 1); | |
| 413 | try testing.expectEqual( | |
| 414 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1) - 1.0, .exact }, | |
| 415 | int.toFloat(Float, .nearest_even), | |
| 416 | ); | |
| 417 | try testing.expectEqual( | |
| 418 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1) - 1.0, .exact }, | |
| 419 | int.toFloat(Float, .away), | |
| 420 | ); | |
| 421 | try testing.expectEqual( | |
| 422 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1) - 1.0, .exact }, | |
| 423 | int.toFloat(Float, .trunc), | |
| 424 | ); | |
| 425 | try testing.expectEqual( | |
| 426 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1) - 1.0, .exact }, | |
| 427 | int.toFloat(Float, .floor), | |
| 428 | ); | |
| 429 | try testing.expectEqual( | |
| 430 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1) - 1.0, .exact }, | |
| 431 | int.toFloat(Float, .ceil), | |
| 432 | ); | |
| 433 | ||
| 434 | int.set(1 << (fractional_bits + 1)); | |
| 435 | try testing.expectEqual( | |
| 436 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 437 | int.toFloat(Float, .nearest_even), | |
| 438 | ); | |
| 439 | try testing.expectEqual( | |
| 440 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 441 | int.toFloat(Float, .away), | |
| 442 | ); | |
| 443 | try testing.expectEqual( | |
| 444 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 445 | int.toFloat(Float, .trunc), | |
| 446 | ); | |
| 447 | try testing.expectEqual( | |
| 448 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 449 | int.toFloat(Float, .floor), | |
| 450 | ); | |
| 451 | try testing.expectEqual( | |
| 452 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1), .exact }, | |
| 453 | int.toFloat(Float, .ceil), | |
| 454 | ); | |
| 455 | ||
| 456 | int.set((1 << (fractional_bits + 1)) + 1); | |
| 457 | try testing.expectEqual( | |
| 458 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1), .inexact }, | |
| 459 | int.toFloat(Float, .nearest_even), | |
| 460 | ); | |
| 461 | try testing.expectEqual( | |
| 462 | Result{ comptime std.math.nextAfter( | |
| 463 | Float, | |
| 464 | std.math.ldexp(@as(Float, 1), fractional_bits + 1), | |
| 465 | std.math.inf(Float), | |
| 466 | ), .inexact }, | |
| 467 | int.toFloat(Float, .away), | |
| 468 | ); | |
| 469 | try testing.expectEqual( | |
| 470 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1), .inexact }, | |
| 471 | int.toFloat(Float, .trunc), | |
| 472 | ); | |
| 473 | try testing.expectEqual( | |
| 474 | Result{ comptime std.math.ldexp(@as(Float, 1), fractional_bits + 1), .inexact }, | |
| 475 | int.toFloat(Float, .floor), | |
| 476 | ); | |
| 477 | try testing.expectEqual( | |
| 478 | Result{ comptime std.math.nextAfter( | |
| 479 | Float, | |
| 480 | std.math.ldexp(@as(Float, 1), fractional_bits + 1), | |
| 481 | std.math.inf(Float), | |
| 482 | ), .inexact }, | |
| 483 | int.toFloat(Float, .ceil), | |
| 484 | ); | |
| 485 | } | |
| 486 | test toFloat { | |
| 487 | try toFloat(f16); | |
| 488 | try toFloat(f32); | |
| 489 | try toFloat(f64); | |
| 490 | try toFloat(f80); | |
| 491 | try toFloat(f128); | |
| 492 | try toFloat(c_longdouble); | |
| 493 | } | |
| 494 | ||
| 88 | 495 | test "normalize" { |
| 89 | 496 | var a = try Managed.init(testing.allocator); |
| 90 | 497 | defer a.deinit(); |
lib/std/math/big/rational.zig deleted-820| ... | ... | @@ -1,820 +0,0 @@ |
| 1 | const std = @import("../../std.zig"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const debug = std.debug; | |
| 4 | const math = std.math; | |
| 5 | const mem = std.mem; | |
| 6 | const testing = std.testing; | |
| 7 | const Allocator = mem.Allocator; | |
| 8 | ||
| 9 | const Limb = std.math.big.Limb; | |
| 10 | const DoubleLimb = std.math.big.DoubleLimb; | |
| 11 | const Int = std.math.big.int.Managed; | |
| 12 | const IntConst = std.math.big.int.Const; | |
| 13 | ||
| 14 | /// An arbitrary-precision rational number. | |
| 15 | /// | |
| 16 | /// Memory is allocated as needed for operations to ensure full precision is kept. The precision | |
| 17 | /// of a Rational is only bounded by memory. | |
| 18 | /// | |
| 19 | /// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers, | |
| 20 | /// gcd(p, q) = 1 always. | |
| 21 | /// | |
| 22 | /// TODO rework this to store its own allocator and use a non-managed big int, to avoid double | |
| 23 | /// allocator storage. | |
| 24 | pub const Rational = struct { | |
| 25 | /// Numerator. Determines the sign of the Rational. | |
| 26 | p: Int, | |
| 27 | ||
| 28 | /// Denominator. Sign is ignored. | |
| 29 | q: Int, | |
| 30 | ||
| 31 | /// Create a new Rational. A small amount of memory will be allocated on initialization. | |
| 32 | /// This will be 2 * Int.default_capacity. | |
| 33 | pub fn init(a: Allocator) !Rational { | |
| 34 | var p = try Int.init(a); | |
| 35 | errdefer p.deinit(); | |
| 36 | return Rational{ | |
| 37 | .p = p, | |
| 38 | .q = try Int.initSet(a, 1), | |
| 39 | }; | |
| 40 | } | |
| 41 | ||
| 42 | /// Frees all memory associated with a Rational. | |
| 43 | pub fn deinit(self: *Rational) void { | |
| 44 | self.p.deinit(); | |
| 45 | self.q.deinit(); | |
| 46 | } | |
| 47 | ||
| 48 | /// Set a Rational from a primitive integer type. | |
| 49 | pub fn setInt(self: *Rational, a: anytype) !void { | |
| 50 | try self.p.set(a); | |
| 51 | try self.q.set(1); | |
| 52 | } | |
| 53 | ||
| 54 | /// Set a Rational from a string of the form `A/B` where A and B are base-10 integers. | |
| 55 | pub fn setFloatString(self: *Rational, str: []const u8) !void { | |
| 56 | // TODO: Accept a/b fractions and exponent form | |
| 57 | if (str.len == 0) { | |
| 58 | return error.InvalidFloatString; | |
| 59 | } | |
| 60 | ||
| 61 | const State = enum { | |
| 62 | Integer, | |
| 63 | Fractional, | |
| 64 | }; | |
| 65 | ||
| 66 | var state = State.Integer; | |
| 67 | var point: ?usize = null; | |
| 68 | ||
| 69 | var start: usize = 0; | |
| 70 | if (str[0] == '-') { | |
| 71 | start += 1; | |
| 72 | } | |
| 73 | ||
| 74 | for (str, 0..) |c, i| { | |
| 75 | switch (state) { | |
| 76 | State.Integer => { | |
| 77 | switch (c) { | |
| 78 | '.' => { | |
| 79 | state = State.Fractional; | |
| 80 | point = i; | |
| 81 | }, | |
| 82 | '0'...'9' => { | |
| 83 | // okay | |
| 84 | }, | |
| 85 | else => { | |
| 86 | return error.InvalidFloatString; | |
| 87 | }, | |
| 88 | } | |
| 89 | }, | |
| 90 | State.Fractional => { | |
| 91 | switch (c) { | |
| 92 | '0'...'9' => { | |
| 93 | // okay | |
| 94 | }, | |
| 95 | else => { | |
| 96 | return error.InvalidFloatString; | |
| 97 | }, | |
| 98 | } | |
| 99 | }, | |
| 100 | } | |
| 101 | } | |
| 102 | ||
| 103 | // TODO: batch the multiplies by 10 | |
| 104 | if (point) |i| { | |
| 105 | try self.p.setString(10, str[0..i]); | |
| 106 | ||
| 107 | const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true }; | |
| 108 | var local_buf: [@sizeOf(Limb) * Int.default_capacity]u8 align(@alignOf(Limb)) = undefined; | |
| 109 | var fba = std.heap.FixedBufferAllocator.init(&local_buf); | |
| 110 | const base_managed = try base.toManaged(fba.allocator()); | |
| 111 | ||
| 112 | var j: usize = start; | |
| 113 | while (j < str.len - i - 1) : (j += 1) { | |
| 114 | try self.p.ensureMulCapacity(self.p.toConst(), base); | |
| 115 | try self.p.mul(&self.p, &base_managed); | |
| 116 | } | |
| 117 | ||
| 118 | try self.q.setString(10, str[i + 1 ..]); | |
| 119 | try self.p.add(&self.p, &self.q); | |
| 120 | ||
| 121 | try self.q.set(1); | |
| 122 | var k: usize = i + 1; | |
| 123 | while (k < str.len) : (k += 1) { | |
| 124 | try self.q.mul(&self.q, &base_managed); | |
| 125 | } | |
| 126 | ||
| 127 | try self.reduce(); | |
| 128 | } else { | |
| 129 | try self.p.setString(10, str[0..]); | |
| 130 | try self.q.set(1); | |
| 131 | } | |
| 132 | } | |
| 133 | ||
| 134 | /// Set a Rational from a floating-point value. The rational will have enough precision to | |
| 135 | /// completely represent the provided float. | |
| 136 | pub fn setFloat(self: *Rational, comptime T: type, f: T) !void { | |
| 137 | // Translated from golang.go/src/math/big/rat.go. | |
| 138 | debug.assert(@typeInfo(T) == .float); | |
| 139 | ||
| 140 | const UnsignedInt = std.meta.Int(.unsigned, @typeInfo(T).float.bits); | |
| 141 | const f_bits = @as(UnsignedInt, @bitCast(f)); | |
| 142 | ||
| 143 | const exponent_bits = math.floatExponentBits(T); | |
| 144 | const exponent_bias = (1 << (exponent_bits - 1)) - 1; | |
| 145 | const mantissa_bits = math.floatMantissaBits(T); | |
| 146 | ||
| 147 | const exponent_mask = (1 << exponent_bits) - 1; | |
| 148 | const mantissa_mask = (1 << mantissa_bits) - 1; | |
| 149 | ||
| 150 | var exponent = @as(i16, @intCast((f_bits >> mantissa_bits) & exponent_mask)); | |
| 151 | var mantissa = f_bits & mantissa_mask; | |
| 152 | ||
| 153 | switch (exponent) { | |
| 154 | exponent_mask => { | |
| 155 | return error.NonFiniteFloat; | |
| 156 | }, | |
| 157 | 0 => { | |
| 158 | // denormal | |
| 159 | exponent -= exponent_bias - 1; | |
| 160 | }, | |
| 161 | else => { | |
| 162 | // normal | |
| 163 | mantissa |= 1 << mantissa_bits; | |
| 164 | exponent -= exponent_bias; | |
| 165 | }, | |
| 166 | } | |
| 167 | ||
| 168 | var shift: i16 = mantissa_bits - exponent; | |
| 169 | ||
| 170 | // factor out powers of two early from rational | |
| 171 | while (mantissa & 1 == 0 and shift > 0) { | |
| 172 | mantissa >>= 1; | |
| 173 | shift -= 1; | |
| 174 | } | |
| 175 | ||
| 176 | try self.p.set(mantissa); | |
| 177 | self.p.setSign(f >= 0); | |
| 178 | ||
| 179 | try self.q.set(1); | |
| 180 | if (shift >= 0) { | |
| 181 | try self.q.shiftLeft(&self.q, @as(usize, @intCast(shift))); | |
| 182 | } else { | |
| 183 | try self.p.shiftLeft(&self.p, @as(usize, @intCast(-shift))); | |
| 184 | } | |
| 185 | ||
| 186 | try self.reduce(); | |
| 187 | } | |
| 188 | ||
| 189 | /// Return a floating-point value that is the closest value to a Rational. | |
| 190 | /// | |
| 191 | /// The result may not be exact if the Rational is too precise or too large for the | |
| 192 | /// target type. | |
| 193 | pub fn toFloat(self: Rational, comptime T: type) !T { | |
| 194 | // Translated from golang.go/src/math/big/rat.go. | |
| 195 | // TODO: Indicate whether the result is not exact. | |
| 196 | debug.assert(@typeInfo(T) == .float); | |
| 197 | ||
| 198 | const fsize = @typeInfo(T).float.bits; | |
| 199 | const BitReprType = std.meta.Int(.unsigned, fsize); | |
| 200 | ||
| 201 | const msize = math.floatMantissaBits(T); | |
| 202 | const msize1 = msize + 1; | |
| 203 | const msize2 = msize1 + 1; | |
| 204 | ||
| 205 | const esize = math.floatExponentBits(T); | |
| 206 | const ebias = (1 << (esize - 1)) - 1; | |
| 207 | const emin = 1 - ebias; | |
| 208 | ||
| 209 | if (self.p.eqlZero()) { | |
| 210 | return 0; | |
| 211 | } | |
| 212 | ||
| 213 | // 1. left-shift a or sub so that a/b is in [1 << msize1, 1 << (msize2 + 1)] | |
| 214 | var exp = @as(isize, @intCast(self.p.bitCountTwosComp())) - @as(isize, @intCast(self.q.bitCountTwosComp())); | |
| 215 | ||
| 216 | var a2 = try self.p.clone(); | |
| 217 | defer a2.deinit(); | |
| 218 | ||
| 219 | var b2 = try self.q.clone(); | |
| 220 | defer b2.deinit(); | |
| 221 | ||
| 222 | const shift = msize2 - exp; | |
| 223 | if (shift >= 0) { | |
| 224 | try a2.shiftLeft(&a2, @as(usize, @intCast(shift))); | |
| 225 | } else { | |
| 226 | try b2.shiftLeft(&b2, @as(usize, @intCast(-shift))); | |
| 227 | } | |
| 228 | ||
| 229 | // 2. compute quotient and remainder | |
| 230 | var q = try Int.init(self.p.allocator); | |
| 231 | defer q.deinit(); | |
| 232 | ||
| 233 | // unused | |
| 234 | var r = try Int.init(self.p.allocator); | |
| 235 | defer r.deinit(); | |
| 236 | ||
| 237 | try Int.divTrunc(&q, &r, &a2, &b2); | |
| 238 | ||
| 239 | var mantissa = extractLowBits(q, BitReprType); | |
| 240 | var have_rem = r.len() > 0; | |
| 241 | ||
| 242 | // 3. q didn't fit in msize2 bits, redo division b2 << 1 | |
| 243 | if (mantissa >> msize2 == 1) { | |
| 244 | if (mantissa & 1 == 1) { | |
| 245 | have_rem = true; | |
| 246 | } | |
| 247 | mantissa >>= 1; | |
| 248 | exp += 1; | |
| 249 | } | |
| 250 | if (mantissa >> msize1 != 1) { | |
| 251 | // NOTE: This can be hit if the limb size is small (u8/16). | |
| 252 | @panic("unexpected bits in result"); | |
| 253 | } | |
| 254 | ||
| 255 | // 4. Rounding | |
| 256 | if (emin - msize <= exp and exp <= emin) { | |
| 257 | // denormal | |
| 258 | const shift1 = @as(math.Log2Int(BitReprType), @intCast(emin - (exp - 1))); | |
| 259 | const lost_bits = mantissa & ((@as(BitReprType, @intCast(1)) << shift1) - 1); | |
| 260 | have_rem = have_rem or lost_bits != 0; | |
| 261 | mantissa >>= shift1; | |
| 262 | exp = 2 - ebias; | |
| 263 | } | |
| 264 | ||
| 265 | // round q using round-half-to-even | |
| 266 | var exact = !have_rem; | |
| 267 | if (mantissa & 1 != 0) { | |
| 268 | exact = false; | |
| 269 | if (have_rem or (mantissa & 2 != 0)) { | |
| 270 | mantissa += 1; | |
| 271 | if (mantissa >= 1 << msize2) { | |
| 272 | // 11...1 => 100...0 | |
| 273 | mantissa >>= 1; | |
| 274 | exp += 1; | |
| 275 | } | |
| 276 | } | |
| 277 | } | |
| 278 | mantissa >>= 1; | |
| 279 | ||
| 280 | const f = math.scalbn(@as(T, @floatFromInt(mantissa)), @as(i32, @intCast(exp - msize1))); | |
| 281 | if (math.isInf(f)) { | |
| 282 | exact = false; | |
| 283 | } | |
| 284 | ||
| 285 | return if (self.p.isPositive()) f else -f; | |
| 286 | } | |
| 287 | ||
| 288 | /// Set a rational from an integer ratio. | |
| 289 | pub fn setRatio(self: *Rational, p: anytype, q: anytype) !void { | |
| 290 | try self.p.set(p); | |
| 291 | try self.q.set(q); | |
| 292 | ||
| 293 | self.p.setSign(@intFromBool(self.p.isPositive()) ^ @intFromBool(self.q.isPositive()) == 0); | |
| 294 | self.q.setSign(true); | |
| 295 | ||
| 296 | try self.reduce(); | |
| 297 | ||
| 298 | if (self.q.eqlZero()) { | |
| 299 | @panic("cannot set rational with denominator = 0"); | |
| 300 | } | |
| 301 | } | |
| 302 | ||
| 303 | /// Set a Rational directly from an Int. | |
| 304 | pub fn copyInt(self: *Rational, a: Int) !void { | |
| 305 | try self.p.copy(a.toConst()); | |
| 306 | try self.q.set(1); | |
| 307 | } | |
| 308 | ||
| 309 | /// Set a Rational directly from a ratio of two Int's. | |
| 310 | pub fn copyRatio(self: *Rational, a: Int, b: Int) !void { | |
| 311 | try self.p.copy(a.toConst()); | |
| 312 | try self.q.copy(b.toConst()); | |
| 313 | ||
| 314 | self.p.setSign(@intFromBool(self.p.isPositive()) ^ @intFromBool(self.q.isPositive()) == 0); | |
| 315 | self.q.setSign(true); | |
| 316 | ||
| 317 | try self.reduce(); | |
| 318 | } | |
| 319 | ||
| 320 | /// Make a Rational positive. | |
| 321 | pub fn abs(r: *Rational) void { | |
| 322 | r.p.abs(); | |
| 323 | } | |
| 324 | ||
| 325 | /// Negate the sign of a Rational. | |
| 326 | pub fn negate(r: *Rational) void { | |
| 327 | r.p.negate(); | |
| 328 | } | |
| 329 | ||
| 330 | /// Efficiently swap a Rational with another. This swaps the limb pointers and a full copy is not | |
| 331 | /// performed. The address of the limbs field will not be the same after this function. | |
| 332 | pub fn swap(r: *Rational, other: *Rational) void { | |
| 333 | r.p.swap(&other.p); | |
| 334 | r.q.swap(&other.q); | |
| 335 | } | |
| 336 | ||
| 337 | /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or | |
| 338 | /// a > b respectively. | |
| 339 | pub fn order(a: Rational, b: Rational) !math.Order { | |
| 340 | return cmpInternal(a, b, false); | |
| 341 | } | |
| 342 | ||
| 343 | /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == | |
| 344 | /// |b| or |a| > |b| respectively. | |
| 345 | pub fn orderAbs(a: Rational, b: Rational) !math.Order { | |
| 346 | return cmpInternal(a, b, true); | |
| 347 | } | |
| 348 | ||
| 349 | // p/q > x/y iff p*y > x*q | |
| 350 | fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order { | |
| 351 | // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid | |
| 352 | // the memory allocations here? | |
| 353 | var q = try Int.init(a.p.allocator); | |
| 354 | defer q.deinit(); | |
| 355 | ||
| 356 | var p = try Int.init(b.p.allocator); | |
| 357 | defer p.deinit(); | |
| 358 | ||
| 359 | try q.mul(&a.p, &b.q); | |
| 360 | try p.mul(&b.p, &a.q); | |
| 361 | ||
| 362 | return if (is_abs) q.orderAbs(p) else q.order(p); | |
| 363 | } | |
| 364 | ||
| 365 | /// rma = a + b. | |
| 366 | /// | |
| 367 | /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. | |
| 368 | /// | |
| 369 | /// Returns an error if memory could not be allocated. | |
| 370 | pub fn add(rma: *Rational, a: Rational, b: Rational) !void { | |
| 371 | var r = rma; | |
| 372 | var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr; | |
| 373 | ||
| 374 | var sr: Rational = undefined; | |
| 375 | if (aliased) { | |
| 376 | sr = try Rational.init(rma.p.allocator); | |
| 377 | r = &sr; | |
| 378 | aliased = true; | |
| 379 | } | |
| 380 | defer if (aliased) { | |
| 381 | rma.swap(r); | |
| 382 | r.deinit(); | |
| 383 | }; | |
| 384 | ||
| 385 | try r.p.mul(&a.p, &b.q); | |
| 386 | try r.q.mul(&b.p, &a.q); | |
| 387 | try r.p.add(&r.p, &r.q); | |
| 388 | ||
| 389 | try r.q.mul(&a.q, &b.q); | |
| 390 | try r.reduce(); | |
| 391 | } | |
| 392 | ||
| 393 | /// rma = a - b. | |
| 394 | /// | |
| 395 | /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. | |
| 396 | /// | |
| 397 | /// Returns an error if memory could not be allocated. | |
| 398 | pub fn sub(rma: *Rational, a: Rational, b: Rational) !void { | |
| 399 | var r = rma; | |
| 400 | var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr; | |
| 401 | ||
| 402 | var sr: Rational = undefined; | |
| 403 | if (aliased) { | |
| 404 | sr = try Rational.init(rma.p.allocator); | |
| 405 | r = &sr; | |
| 406 | aliased = true; | |
| 407 | } | |
| 408 | defer if (aliased) { | |
| 409 | rma.swap(r); | |
| 410 | r.deinit(); | |
| 411 | }; | |
| 412 | ||
| 413 | try r.p.mul(&a.p, &b.q); | |
| 414 | try r.q.mul(&b.p, &a.q); | |
| 415 | try r.p.sub(&r.p, &r.q); | |
| 416 | ||
| 417 | try r.q.mul(&a.q, &b.q); | |
| 418 | try r.reduce(); | |
| 419 | } | |
| 420 | ||
| 421 | /// rma = a * b. | |
| 422 | /// | |
| 423 | /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. | |
| 424 | /// | |
| 425 | /// Returns an error if memory could not be allocated. | |
| 426 | pub fn mul(r: *Rational, a: Rational, b: Rational) !void { | |
| 427 | try r.p.mul(&a.p, &b.p); | |
| 428 | try r.q.mul(&a.q, &b.q); | |
| 429 | try r.reduce(); | |
| 430 | } | |
| 431 | ||
| 432 | /// rma = a / b. | |
| 433 | /// | |
| 434 | /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. | |
| 435 | /// | |
| 436 | /// Returns an error if memory could not be allocated. | |
| 437 | pub fn div(r: *Rational, a: Rational, b: Rational) !void { | |
| 438 | if (b.p.eqlZero()) { | |
| 439 | @panic("division by zero"); | |
| 440 | } | |
| 441 | ||
| 442 | try r.p.mul(&a.p, &b.q); | |
| 443 | try r.q.mul(&b.p, &a.q); | |
| 444 | try r.reduce(); | |
| 445 | } | |
| 446 | ||
| 447 | /// Invert the numerator and denominator fields of a Rational. p/q => q/p. | |
| 448 | pub fn invert(r: *Rational) void { | |
| 449 | Int.swap(&r.p, &r.q); | |
| 450 | } | |
| 451 | ||
| 452 | // reduce r/q such that gcd(r, q) = 1 | |
| 453 | fn reduce(r: *Rational) !void { | |
| 454 | var a = try Int.init(r.p.allocator); | |
| 455 | defer a.deinit(); | |
| 456 | ||
| 457 | const sign = r.p.isPositive(); | |
| 458 | r.p.abs(); | |
| 459 | try a.gcd(&r.p, &r.q); | |
| 460 | r.p.setSign(sign); | |
| 461 | ||
| 462 | const one = IntConst{ .limbs = &[_]Limb{1}, .positive = true }; | |
| 463 | if (a.toConst().order(one) != .eq) { | |
| 464 | var unused = try Int.init(r.p.allocator); | |
| 465 | defer unused.deinit(); | |
| 466 | ||
| 467 | // TODO: divexact would be useful here | |
| 468 | // TODO: don't copy r.q for div | |
| 469 | try Int.divTrunc(&r.p, &unused, &r.p, &a); | |
| 470 | try Int.divTrunc(&r.q, &unused, &r.q, &a); | |
| 471 | } | |
| 472 | } | |
| 473 | }; | |
| 474 | ||
| 475 | fn extractLowBits(a: Int, comptime T: type) T { | |
| 476 | debug.assert(@typeInfo(T) == .int); | |
| 477 | ||
| 478 | const t_bits = @typeInfo(T).int.bits; | |
| 479 | const limb_bits = @typeInfo(Limb).int.bits; | |
| 480 | if (t_bits <= limb_bits) { | |
| 481 | return @as(T, @truncate(a.limbs[0])); | |
| 482 | } else { | |
| 483 | var r: T = 0; | |
| 484 | comptime var i: usize = 0; | |
| 485 | ||
| 486 | // Remainder is always 0 since if t_bits >= limb_bits -> Limb | T and both | |
| 487 | // are powers of two. | |
| 488 | inline while (i < t_bits / limb_bits) : (i += 1) { | |
| 489 | r |= math.shl(T, a.limbs[i], i * limb_bits); | |
| 490 | } | |
| 491 | ||
| 492 | return r; | |
| 493 | } | |
| 494 | } | |
| 495 | ||
| 496 | test extractLowBits { | |
| 497 | var a = try Int.initSet(testing.allocator, 0x11112222333344441234567887654321); | |
| 498 | defer a.deinit(); | |
| 499 | ||
| 500 | const a1 = extractLowBits(a, u8); | |
| 501 | try testing.expect(a1 == 0x21); | |
| 502 | ||
| 503 | const a2 = extractLowBits(a, u16); | |
| 504 | try testing.expect(a2 == 0x4321); | |
| 505 | ||
| 506 | const a3 = extractLowBits(a, u32); | |
| 507 | try testing.expect(a3 == 0x87654321); | |
| 508 | ||
| 509 | const a4 = extractLowBits(a, u64); | |
| 510 | try testing.expect(a4 == 0x1234567887654321); | |
| 511 | ||
| 512 | const a5 = extractLowBits(a, u128); | |
| 513 | try testing.expect(a5 == 0x11112222333344441234567887654321); | |
| 514 | } | |
| 515 | ||
| 516 | test "set" { | |
| 517 | var a = try Rational.init(testing.allocator); | |
| 518 | defer a.deinit(); | |
| 519 | ||
| 520 | try a.setInt(5); | |
| 521 | try testing.expect((try a.p.toInt(u32)) == 5); | |
| 522 | try testing.expect((try a.q.toInt(u32)) == 1); | |
| 523 | ||
| 524 | try a.setRatio(7, 3); | |
| 525 | try testing.expect((try a.p.toInt(u32)) == 7); | |
| 526 | try testing.expect((try a.q.toInt(u32)) == 3); | |
| 527 | ||
| 528 | try a.setRatio(9, 3); | |
| 529 | try testing.expect((try a.p.toInt(i32)) == 3); | |
| 530 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 531 | ||
| 532 | try a.setRatio(-9, 3); | |
| 533 | try testing.expect((try a.p.toInt(i32)) == -3); | |
| 534 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 535 | ||
| 536 | try a.setRatio(9, -3); | |
| 537 | try testing.expect((try a.p.toInt(i32)) == -3); | |
| 538 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 539 | ||
| 540 | try a.setRatio(-9, -3); | |
| 541 | try testing.expect((try a.p.toInt(i32)) == 3); | |
| 542 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 543 | } | |
| 544 | ||
| 545 | test "setFloat" { | |
| 546 | var a = try Rational.init(testing.allocator); | |
| 547 | defer a.deinit(); | |
| 548 | ||
| 549 | try a.setFloat(f64, 2.5); | |
| 550 | try testing.expect((try a.p.toInt(i32)) == 5); | |
| 551 | try testing.expect((try a.q.toInt(i32)) == 2); | |
| 552 | ||
| 553 | try a.setFloat(f32, -2.5); | |
| 554 | try testing.expect((try a.p.toInt(i32)) == -5); | |
| 555 | try testing.expect((try a.q.toInt(i32)) == 2); | |
| 556 | ||
| 557 | try a.setFloat(f32, 3.141593); | |
| 558 | ||
| 559 | // = 3.14159297943115234375 | |
| 560 | try testing.expect((try a.p.toInt(u32)) == 3294199); | |
| 561 | try testing.expect((try a.q.toInt(u32)) == 1048576); | |
| 562 | ||
| 563 | try a.setFloat(f64, 72.141593120712409172417410926841290461290467124); | |
| 564 | ||
| 565 | // = 72.1415931207124145885245525278151035308837890625 | |
| 566 | try testing.expect((try a.p.toInt(u128)) == 5076513310880537); | |
| 567 | try testing.expect((try a.q.toInt(u128)) == 70368744177664); | |
| 568 | } | |
| 569 | ||
| 570 | test "setFloatString" { | |
| 571 | var a = try Rational.init(testing.allocator); | |
| 572 | defer a.deinit(); | |
| 573 | ||
| 574 | try a.setFloatString("72.14159312071241458852455252781510353"); | |
| 575 | ||
| 576 | // = 72.1415931207124145885245525278151035308837890625 | |
| 577 | try testing.expect((try a.p.toInt(u128)) == 7214159312071241458852455252781510353); | |
| 578 | try testing.expect((try a.q.toInt(u128)) == 100000000000000000000000000000000000); | |
| 579 | } | |
| 580 | ||
| 581 | test "toFloat" { | |
| 582 | var a = try Rational.init(testing.allocator); | |
| 583 | defer a.deinit(); | |
| 584 | ||
| 585 | // = 3.14159297943115234375 | |
| 586 | try a.setRatio(3294199, 1048576); | |
| 587 | try testing.expect((try a.toFloat(f64)) == 3.14159297943115234375); | |
| 588 | ||
| 589 | // = 72.1415931207124145885245525278151035308837890625 | |
| 590 | try a.setRatio(5076513310880537, 70368744177664); | |
| 591 | try testing.expect((try a.toFloat(f64)) == 72.141593120712409172417410926841290461290467124); | |
| 592 | } | |
| 593 | ||
| 594 | test "set/to Float round-trip" { | |
| 595 | var a = try Rational.init(testing.allocator); | |
| 596 | defer a.deinit(); | |
| 597 | var prng = std.Random.DefaultPrng.init(std.testing.random_seed); | |
| 598 | const random = prng.random(); | |
| 599 | var i: usize = 0; | |
| 600 | while (i < 512) : (i += 1) { | |
| 601 | const r = random.float(f64); | |
| 602 | try a.setFloat(f64, r); | |
| 603 | try testing.expect((try a.toFloat(f64)) == r); | |
| 604 | } | |
| 605 | } | |
| 606 | ||
| 607 | test "copy" { | |
| 608 | var a = try Rational.init(testing.allocator); | |
| 609 | defer a.deinit(); | |
| 610 | ||
| 611 | var b = try Int.initSet(testing.allocator, 5); | |
| 612 | defer b.deinit(); | |
| 613 | ||
| 614 | try a.copyInt(b); | |
| 615 | try testing.expect((try a.p.toInt(u32)) == 5); | |
| 616 | try testing.expect((try a.q.toInt(u32)) == 1); | |
| 617 | ||
| 618 | var c = try Int.initSet(testing.allocator, 7); | |
| 619 | defer c.deinit(); | |
| 620 | var d = try Int.initSet(testing.allocator, 3); | |
| 621 | defer d.deinit(); | |
| 622 | ||
| 623 | try a.copyRatio(c, d); | |
| 624 | try testing.expect((try a.p.toInt(u32)) == 7); | |
| 625 | try testing.expect((try a.q.toInt(u32)) == 3); | |
| 626 | ||
| 627 | var e = try Int.initSet(testing.allocator, 9); | |
| 628 | defer e.deinit(); | |
| 629 | var f = try Int.initSet(testing.allocator, 3); | |
| 630 | defer f.deinit(); | |
| 631 | ||
| 632 | try a.copyRatio(e, f); | |
| 633 | try testing.expect((try a.p.toInt(u32)) == 3); | |
| 634 | try testing.expect((try a.q.toInt(u32)) == 1); | |
| 635 | } | |
| 636 | ||
| 637 | test "negate" { | |
| 638 | var a = try Rational.init(testing.allocator); | |
| 639 | defer a.deinit(); | |
| 640 | ||
| 641 | try a.setInt(-50); | |
| 642 | try testing.expect((try a.p.toInt(i32)) == -50); | |
| 643 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 644 | ||
| 645 | a.negate(); | |
| 646 | try testing.expect((try a.p.toInt(i32)) == 50); | |
| 647 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 648 | ||
| 649 | a.negate(); | |
| 650 | try testing.expect((try a.p.toInt(i32)) == -50); | |
| 651 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 652 | } | |
| 653 | ||
| 654 | test "abs" { | |
| 655 | var a = try Rational.init(testing.allocator); | |
| 656 | defer a.deinit(); | |
| 657 | ||
| 658 | try a.setInt(-50); | |
| 659 | try testing.expect((try a.p.toInt(i32)) == -50); | |
| 660 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 661 | ||
| 662 | a.abs(); | |
| 663 | try testing.expect((try a.p.toInt(i32)) == 50); | |
| 664 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 665 | ||
| 666 | a.abs(); | |
| 667 | try testing.expect((try a.p.toInt(i32)) == 50); | |
| 668 | try testing.expect((try a.q.toInt(i32)) == 1); | |
| 669 | } | |
| 670 | ||
| 671 | test "swap" { | |
| 672 | var a = try Rational.init(testing.allocator); | |
| 673 | defer a.deinit(); | |
| 674 | var b = try Rational.init(testing.allocator); | |
| 675 | defer b.deinit(); | |
| 676 | ||
| 677 | try a.setRatio(50, 23); | |
| 678 | try b.setRatio(17, 3); | |
| 679 | ||
| 680 | try testing.expect((try a.p.toInt(u32)) == 50); | |
| 681 | try testing.expect((try a.q.toInt(u32)) == 23); | |
| 682 | ||
| 683 | try testing.expect((try b.p.toInt(u32)) == 17); | |
| 684 | try testing.expect((try b.q.toInt(u32)) == 3); | |
| 685 | ||
| 686 | a.swap(&b); | |
| 687 | ||
| 688 | try testing.expect((try a.p.toInt(u32)) == 17); | |
| 689 | try testing.expect((try a.q.toInt(u32)) == 3); | |
| 690 | ||
| 691 | try testing.expect((try b.p.toInt(u32)) == 50); | |
| 692 | try testing.expect((try b.q.toInt(u32)) == 23); | |
| 693 | } | |
| 694 | ||
| 695 | test "order" { | |
| 696 | var a = try Rational.init(testing.allocator); | |
| 697 | defer a.deinit(); | |
| 698 | var b = try Rational.init(testing.allocator); | |
| 699 | defer b.deinit(); | |
| 700 | ||
| 701 | try a.setRatio(500, 231); | |
| 702 | try b.setRatio(18903, 8584); | |
| 703 | try testing.expect((try a.order(b)) == .lt); | |
| 704 | ||
| 705 | try a.setRatio(890, 10); | |
| 706 | try b.setRatio(89, 1); | |
| 707 | try testing.expect((try a.order(b)) == .eq); | |
| 708 | } | |
| 709 | ||
| 710 | test "order/orderAbs with negative" { | |
| 711 | var a = try Rational.init(testing.allocator); | |
| 712 | defer a.deinit(); | |
| 713 | var b = try Rational.init(testing.allocator); | |
| 714 | defer b.deinit(); | |
| 715 | ||
| 716 | try a.setRatio(1, 1); | |
| 717 | try b.setRatio(-2, 1); | |
| 718 | try testing.expect((try a.order(b)) == .gt); | |
| 719 | try testing.expect((try a.orderAbs(b)) == .lt); | |
| 720 | } | |
| 721 | ||
| 722 | test "add single-limb" { | |
| 723 | var a = try Rational.init(testing.allocator); | |
| 724 | defer a.deinit(); | |
| 725 | var b = try Rational.init(testing.allocator); | |
| 726 | defer b.deinit(); | |
| 727 | ||
| 728 | try a.setRatio(500, 231); | |
| 729 | try b.setRatio(18903, 8584); | |
| 730 | try testing.expect((try a.order(b)) == .lt); | |
| 731 | ||
| 732 | try a.setRatio(890, 10); | |
| 733 | try b.setRatio(89, 1); | |
| 734 | try testing.expect((try a.order(b)) == .eq); | |
| 735 | } | |
| 736 | ||
| 737 | test "add" { | |
| 738 | var a = try Rational.init(testing.allocator); | |
| 739 | defer a.deinit(); | |
| 740 | var b = try Rational.init(testing.allocator); | |
| 741 | defer b.deinit(); | |
| 742 | var r = try Rational.init(testing.allocator); | |
| 743 | defer r.deinit(); | |
| 744 | ||
| 745 | try a.setRatio(78923, 23341); | |
| 746 | try b.setRatio(123097, 12441414); | |
| 747 | try a.add(a, b); | |
| 748 | ||
| 749 | try r.setRatio(984786924199, 290395044174); | |
| 750 | try testing.expect((try a.order(r)) == .eq); | |
| 751 | } | |
| 752 | ||
| 753 | test "sub" { | |
| 754 | var a = try Rational.init(testing.allocator); | |
| 755 | defer a.deinit(); | |
| 756 | var b = try Rational.init(testing.allocator); | |
| 757 | defer b.deinit(); | |
| 758 | var r = try Rational.init(testing.allocator); | |
| 759 | defer r.deinit(); | |
| 760 | ||
| 761 | try a.setRatio(78923, 23341); | |
| 762 | try b.setRatio(123097, 12441414); | |
| 763 | try a.sub(a, b); | |
| 764 | ||
| 765 | try r.setRatio(979040510045, 290395044174); | |
| 766 | try testing.expect((try a.order(r)) == .eq); | |
| 767 | } | |
| 768 | ||
| 769 | test "mul" { | |
| 770 | var a = try Rational.init(testing.allocator); | |
| 771 | defer a.deinit(); | |
| 772 | var b = try Rational.init(testing.allocator); | |
| 773 | defer b.deinit(); | |
| 774 | var r = try Rational.init(testing.allocator); | |
| 775 | defer r.deinit(); | |
| 776 | ||
| 777 | try a.setRatio(78923, 23341); | |
| 778 | try b.setRatio(123097, 12441414); | |
| 779 | try a.mul(a, b); | |
| 780 | ||
| 781 | try r.setRatio(571481443, 17082061422); | |
| 782 | try testing.expect((try a.order(r)) == .eq); | |
| 783 | } | |
| 784 | ||
| 785 | test "div" { | |
| 786 | { | |
| 787 | var a = try Rational.init(testing.allocator); | |
| 788 | defer a.deinit(); | |
| 789 | var b = try Rational.init(testing.allocator); | |
| 790 | defer b.deinit(); | |
| 791 | var r = try Rational.init(testing.allocator); | |
| 792 | defer r.deinit(); | |
| 793 | ||
| 794 | try a.setRatio(78923, 23341); | |
| 795 | try b.setRatio(123097, 12441414); | |
| 796 | try a.div(a, b); | |
| 797 | ||
| 798 | try r.setRatio(75531824394, 221015929); | |
| 799 | try testing.expect((try a.order(r)) == .eq); | |
| 800 | } | |
| 801 | ||
| 802 | { | |
| 803 | var a = try Rational.init(testing.allocator); | |
| 804 | defer a.deinit(); | |
| 805 | var r = try Rational.init(testing.allocator); | |
| 806 | defer r.deinit(); | |
| 807 | ||
| 808 | try a.setRatio(78923, 23341); | |
| 809 | a.invert(); | |
| 810 | ||
| 811 | try r.setRatio(23341, 78923); | |
| 812 | try testing.expect((try a.order(r)) == .eq); | |
| 813 | ||
| 814 | try a.setRatio(-78923, 23341); | |
| 815 | a.invert(); | |
| 816 | ||
| 817 | try r.setRatio(-23341, 78923); | |
| 818 | try testing.expect((try a.order(r)) == .eq); | |
| 819 | } | |
| 820 | } |
lib/std/math/float.zig+113| ... | ... | @@ -4,6 +4,119 @@ const assert = std.debug.assert; |
| 4 | 4 | const expect = std.testing.expect; |
| 5 | 5 | const expectEqual = std.testing.expectEqual; |
| 6 | 6 | |
| 7 | pub const Sign = enum(u1) { positive, negative }; | |
| 8 | ||
| 9 | pub fn FloatRepr(comptime Float: type) type { | |
| 10 | const fractional_bits = floatFractionalBits(Float); | |
| 11 | const exponent_bits = floatExponentBits(Float); | |
| 12 | return packed struct { | |
| 13 | const Repr = @This(); | |
| 14 | ||
| 15 | mantissa: StoredMantissa, | |
| 16 | exponent: BiasedExponent, | |
| 17 | sign: Sign, | |
| 18 | ||
| 19 | pub const StoredMantissa = @Type(.{ .int = .{ | |
| 20 | .signedness = .unsigned, | |
| 21 | .bits = floatMantissaBits(Float), | |
| 22 | } }); | |
| 23 | pub const Mantissa = @Type(.{ .int = .{ | |
| 24 | .signedness = .unsigned, | |
| 25 | .bits = 1 + fractional_bits, | |
| 26 | } }); | |
| 27 | pub const Exponent = @Type(.{ .int = .{ | |
| 28 | .signedness = .signed, | |
| 29 | .bits = exponent_bits, | |
| 30 | } }); | |
| 31 | pub const BiasedExponent = enum(@Type(.{ .int = .{ | |
| 32 | .signedness = .unsigned, | |
| 33 | .bits = exponent_bits, | |
| 34 | } })) { | |
| 35 | denormal = 0, | |
| 36 | min_normal = 1, | |
| 37 | zero = (1 << (exponent_bits - 1)) - 1, | |
| 38 | max_normal = (1 << exponent_bits) - 2, | |
| 39 | infinite = (1 << exponent_bits) - 1, | |
| 40 | _, | |
| 41 | ||
| 42 | pub const Int = @typeInfo(BiasedExponent).@"enum".tag_type; | |
| 43 | ||
| 44 | pub fn unbias(biased: BiasedExponent) Exponent { | |
| 45 | switch (biased) { | |
| 46 | .denormal => unreachable, | |
| 47 | else => return @bitCast(@intFromEnum(biased) -% @intFromEnum(BiasedExponent.zero)), | |
| 48 | .infinite => unreachable, | |
| 49 | } | |
| 50 | } | |
| 51 | ||
| 52 | pub fn bias(unbiased: Exponent) BiasedExponent { | |
| 53 | return @enumFromInt(@intFromEnum(BiasedExponent.zero) +% @as(Int, @bitCast(unbiased))); | |
| 54 | } | |
| 55 | }; | |
| 56 | ||
| 57 | pub const Normalized = struct { | |
| 58 | fraction: Fraction, | |
| 59 | exponent: Normalized.Exponent, | |
| 60 | ||
| 61 | pub const Fraction = @Type(.{ .int = .{ | |
| 62 | .signedness = .unsigned, | |
| 63 | .bits = fractional_bits, | |
| 64 | } }); | |
| 65 | pub const Exponent = @Type(.{ .int = .{ | |
| 66 | .signedness = .signed, | |
| 67 | .bits = 1 + exponent_bits, | |
| 68 | } }); | |
| 69 | ||
| 70 | /// This currently truncates denormal values, which needs to be fixed before this can be used to | |
| 71 | /// produce a rounded value. | |
| 72 | pub fn reconstruct(normalized: Normalized, sign: Sign) Float { | |
| 73 | if (normalized.exponent > BiasedExponent.max_normal.unbias()) return @bitCast(Repr{ | |
| 74 | .mantissa = 0, | |
| 75 | .exponent = .infinite, | |
| 76 | .sign = sign, | |
| 77 | }); | |
| 78 | const mantissa = @as(Mantissa, 1 << fractional_bits) | normalized.fraction; | |
| 79 | if (normalized.exponent < BiasedExponent.min_normal.unbias()) return @bitCast(Repr{ | |
| 80 | .mantissa = @truncate(std.math.shr( | |
| 81 | Mantissa, | |
| 82 | mantissa, | |
| 83 | BiasedExponent.min_normal.unbias() - normalized.exponent, | |
| 84 | )), | |
| 85 | .exponent = .denormal, | |
| 86 | .sign = sign, | |
| 87 | }); | |
| 88 | return @bitCast(Repr{ | |
| 89 | .mantissa = @truncate(mantissa), | |
| 90 | .exponent = .bias(@intCast(normalized.exponent)), | |
| 91 | .sign = sign, | |
| 92 | }); | |
| 93 | } | |
| 94 | }; | |
| 95 | ||
| 96 | pub const Classified = union(enum) { normalized: Normalized, infinity, nan, invalid }; | |
| 97 | fn classify(repr: Repr) Classified { | |
| 98 | return switch (repr.exponent) { | |
| 99 | .denormal => { | |
| 100 | const mantissa: Mantissa = repr.mantissa; | |
| 101 | const shift = @clz(mantissa); | |
| 102 | return .{ .normalized = .{ | |
| 103 | .fraction = @truncate(mantissa << shift), | |
| 104 | .exponent = @as(Normalized.Exponent, comptime BiasedExponent.min_normal.unbias()) - shift, | |
| 105 | } }; | |
| 106 | }, | |
| 107 | else => if (repr.mantissa <= std.math.maxInt(Normalized.Fraction)) .{ .normalized = .{ | |
| 108 | .fraction = @intCast(repr.mantissa), | |
| 109 | .exponent = repr.exponent.unbias(), | |
| 110 | } } else .invalid, | |
| 111 | .infinite => switch (repr.mantissa) { | |
| 112 | 0 => .infinity, | |
| 113 | else => .nan, | |
| 114 | }, | |
| 115 | }; | |
| 116 | } | |
| 117 | }; | |
| 118 | } | |
| 119 | ||
| 7 | 120 | /// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic. |
| 8 | 121 | inline fn mantissaOne(comptime T: type) comptime_int { |
| 9 | 122 | return if (@typeInfo(T).float.bits == 80) 1 << floatFractionalBits(T) else 0; |
lib/std/zon/parse.zig+1-1| ... | ... | @@ -593,7 +593,7 @@ const Parser = struct { |
| 593 | 593 | switch (node.get(self.zoir)) { |
| 594 | 594 | .int_literal => |int| switch (int) { |
| 595 | 595 | .small => |val| return @floatFromInt(val), |
| 596 | .big => |val| return val.toFloat(T), | |
| 596 | .big => |val| return val.toFloat(T, .nearest_even)[0], | |
| 597 | 597 | }, |
| 598 | 598 | .float_literal => |val| return @floatCast(val), |
| 599 | 599 | .pos_inf => return std.math.inf(T), |
src/Sema.zig+42-64| ... | ... | @@ -32843,24 +32843,21 @@ fn cmpNumeric( |
| 32843 | 32843 | } |
| 32844 | 32844 | } |
| 32845 | 32845 | if (lhs_is_float) { |
| 32846 | if (lhs_val.floatHasFraction(zcu)) { | |
| 32847 | switch (op) { | |
| 32846 | const float = lhs_val.toFloat(f128, zcu); | |
| 32847 | var big_int: std.math.big.int.Mutable = .{ | |
| 32848 | .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), | |
| 32849 | .len = undefined, | |
| 32850 | .positive = undefined, | |
| 32851 | }; | |
| 32852 | switch (big_int.setFloat(float, .away)) { | |
| 32853 | .inexact => switch (op) { | |
| 32848 | 32854 | .eq => return .bool_false, |
| 32849 | 32855 | .neq => return .bool_true, |
| 32850 | 32856 | else => {}, |
| 32851 | } | |
| 32852 | } | |
| 32853 | ||
| 32854 | var bigint = try float128IntPartToBigInt(sema.gpa, lhs_val.toFloat(f128, zcu)); | |
| 32855 | defer bigint.deinit(); | |
| 32856 | if (lhs_val.floatHasFraction(zcu)) { | |
| 32857 | if (lhs_is_signed) { | |
| 32858 | try bigint.addScalar(&bigint, -1); | |
| 32859 | } else { | |
| 32860 | try bigint.addScalar(&bigint, 1); | |
| 32861 | } | |
| 32857 | }, | |
| 32858 | .exact => {}, | |
| 32862 | 32859 | } |
| 32863 | lhs_bits = bigint.toConst().bitCountTwosComp(); | |
| 32860 | lhs_bits = big_int.toConst().bitCountTwosComp(); | |
| 32864 | 32861 | } else { |
| 32865 | 32862 | lhs_bits = lhs_val.intBitCountTwosComp(zcu); |
| 32866 | 32863 | } |
| ... | ... | @@ -32890,24 +32887,21 @@ fn cmpNumeric( |
| 32890 | 32887 | } |
| 32891 | 32888 | } |
| 32892 | 32889 | if (rhs_is_float) { |
| 32893 | if (rhs_val.floatHasFraction(zcu)) { | |
| 32894 | switch (op) { | |
| 32890 | const float = rhs_val.toFloat(f128, zcu); | |
| 32891 | var big_int: std.math.big.int.Mutable = .{ | |
| 32892 | .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), | |
| 32893 | .len = undefined, | |
| 32894 | .positive = undefined, | |
| 32895 | }; | |
| 32896 | switch (big_int.setFloat(float, .away)) { | |
| 32897 | .inexact => switch (op) { | |
| 32895 | 32898 | .eq => return .bool_false, |
| 32896 | 32899 | .neq => return .bool_true, |
| 32897 | 32900 | else => {}, |
| 32898 | } | |
| 32899 | } | |
| 32900 | ||
| 32901 | var bigint = try float128IntPartToBigInt(sema.gpa, rhs_val.toFloat(f128, zcu)); | |
| 32902 | defer bigint.deinit(); | |
| 32903 | if (rhs_val.floatHasFraction(zcu)) { | |
| 32904 | if (rhs_is_signed) { | |
| 32905 | try bigint.addScalar(&bigint, -1); | |
| 32906 | } else { | |
| 32907 | try bigint.addScalar(&bigint, 1); | |
| 32908 | } | |
| 32901 | }, | |
| 32902 | .exact => {}, | |
| 32909 | 32903 | } |
| 32910 | rhs_bits = bigint.toConst().bitCountTwosComp(); | |
| 32904 | rhs_bits = big_int.toConst().bitCountTwosComp(); | |
| 32911 | 32905 | } else { |
| 32912 | 32906 | rhs_bits = rhs_val.intBitCountTwosComp(zcu); |
| 32913 | 32907 | } |
| ... | ... | @@ -36955,31 +36949,6 @@ fn intFromFloat( |
| 36955 | 36949 | return sema.intFromFloatScalar(block, src, val, int_ty, mode); |
| 36956 | 36950 | } |
| 36957 | 36951 | |
| 36958 | // float is expected to be finite and non-NaN | |
| 36959 | fn float128IntPartToBigInt( | |
| 36960 | arena: Allocator, | |
| 36961 | float: f128, | |
| 36962 | ) !std.math.big.int.Managed { | |
| 36963 | const is_negative = std.math.signbit(float); | |
| 36964 | const floored = @floor(@abs(float)); | |
| 36965 | ||
| 36966 | var rational = try std.math.big.Rational.init(arena); | |
| 36967 | defer rational.q.deinit(); | |
| 36968 | rational.setFloat(f128, floored) catch |err| switch (err) { | |
| 36969 | error.NonFiniteFloat => unreachable, | |
| 36970 | error.OutOfMemory => return error.OutOfMemory, | |
| 36971 | }; | |
| 36972 | ||
| 36973 | // The float is reduced in rational.setFloat, so we assert that denominator is equal to one | |
| 36974 | const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; | |
| 36975 | assert(rational.q.toConst().eqlAbs(big_one)); | |
| 36976 | ||
| 36977 | if (is_negative) { | |
| 36978 | rational.negate(); | |
| 36979 | } | |
| 36980 | return rational.p; | |
| 36981 | } | |
| 36982 | ||
| 36983 | 36952 | fn intFromFloatScalar( |
| 36984 | 36953 | sema: *Sema, |
| 36985 | 36954 | block: *Block, |
| ... | ... | @@ -36993,13 +36962,6 @@ fn intFromFloatScalar( |
| 36993 | 36962 | |
| 36994 | 36963 | if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src); |
| 36995 | 36964 | |
| 36996 | if (mode == .exact and val.floatHasFraction(zcu)) return sema.fail( | |
| 36997 | block, | |
| 36998 | src, | |
| 36999 | "fractional component prevents float value '{}' from coercion to type '{}'", | |
| 37000 | .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) }, | |
| 37001 | ); | |
| 37002 | ||
| 37003 | 36965 | const float = val.toFloat(f128, zcu); |
| 37004 | 36966 | if (std.math.isNan(float)) { |
| 37005 | 36967 | return sema.fail(block, src, "float value NaN cannot be stored in integer type '{}'", .{ |
| ... | ... | @@ -37012,12 +36974,28 @@ fn intFromFloatScalar( |
| 37012 | 36974 | }); |
| 37013 | 36975 | } |
| 37014 | 36976 | |
| 37015 | var big_int = try float128IntPartToBigInt(sema.arena, float); | |
| 37016 | defer big_int.deinit(); | |
| 37017 | ||
| 36977 | var big_int: std.math.big.int.Mutable = .{ | |
| 36978 | .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), | |
| 36979 | .len = undefined, | |
| 36980 | .positive = undefined, | |
| 36981 | }; | |
| 36982 | switch (big_int.setFloat(float, .trunc)) { | |
| 36983 | .inexact => switch (mode) { | |
| 36984 | .exact => return sema.fail( | |
| 36985 | block, | |
| 36986 | src, | |
| 36987 | "fractional component prevents float value '{}' from coercion to type '{}'", | |
| 36988 | .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) }, | |
| 36989 | ), | |
| 36990 | .truncate => {}, | |
| 36991 | }, | |
| 36992 | .exact => {}, | |
| 36993 | } | |
| 37018 | 36994 | const cti_result = try pt.intValue_big(.comptime_int, big_int.toConst()); |
| 36995 | if (int_ty.toIntern() == .comptime_int_type) return cti_result; | |
| 37019 | 36996 | |
| 37020 | if (!(try sema.intFitsInType(cti_result, int_ty, null))) { | |
| 36997 | const int_info = int_ty.intInfo(zcu); | |
| 36998 | if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) { | |
| 37021 | 36999 | return sema.fail(block, src, "float value '{}' cannot be stored in integer type '{}'", .{ |
| 37022 | 37000 | val.fmtValueSema(pt, sema), int_ty.fmt(pt), |
| 37023 | 37001 | }); |
src/Sema/LowerZon.zig+12-19| ... | ... | @@ -509,30 +509,23 @@ fn lowerInt( |
| 509 | 509 | }, |
| 510 | 510 | }, |
| 511 | 511 | .float_literal => |val| { |
| 512 | // Check for fractional components | |
| 513 | if (@rem(val, 1) != 0) { | |
| 514 | return self.fail( | |
| 512 | var big_int: std.math.big.int.Mutable = .{ | |
| 513 | .limbs = try self.sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(val)), | |
| 514 | .len = undefined, | |
| 515 | .positive = undefined, | |
| 516 | }; | |
| 517 | switch (big_int.setFloat(val, .trunc)) { | |
| 518 | .inexact => return self.fail( | |
| 515 | 519 | node, |
| 516 | 520 | "fractional component prevents float value '{}' from coercion to type '{}'", |
| 517 | 521 | .{ val, res_ty.fmt(self.sema.pt) }, |
| 518 | ); | |
| 522 | ), | |
| 523 | .exact => {}, | |
| 519 | 524 | } |
| 520 | 525 | |
| 521 | // Create a rational representation of the float | |
| 522 | var rational = try std.math.big.Rational.init(self.sema.arena); | |
| 523 | rational.setFloat(f128, val) catch |err| switch (err) { | |
| 524 | error.NonFiniteFloat => unreachable, | |
| 525 | error.OutOfMemory => return error.OutOfMemory, | |
| 526 | }; | |
| 527 | ||
| 528 | // The float is reduced in rational.setFloat, so we assert that denominator is equal to | |
| 529 | // one | |
| 530 | const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; | |
| 531 | assert(rational.q.toConst().eqlAbs(big_one)); | |
| 532 | ||
| 533 | 526 | // Check that the result is in range of the result type |
| 534 | 527 | const int_info = res_ty.intInfo(self.sema.pt.zcu); |
| 535 | if (!rational.p.fitsInTwosComp(int_info.signedness, int_info.bits)) { | |
| 528 | if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) { | |
| 536 | 529 | return self.fail( |
| 537 | 530 | node, |
| 538 | 531 | "type '{}' cannot represent integer value '{}'", |
| ... | ... | @@ -543,7 +536,7 @@ fn lowerInt( |
| 543 | 536 | return self.sema.pt.intern(.{ |
| 544 | 537 | .int = .{ |
| 545 | 538 | .ty = res_ty.toIntern(), |
| 546 | .storage = .{ .big_int = rational.p.toConst() }, | |
| 539 | .storage = .{ .big_int = big_int.toConst() }, | |
| 547 | 540 | }, |
| 548 | 541 | }); |
| 549 | 542 | }, |
| ... | ... | @@ -584,7 +577,7 @@ fn lowerFloat( |
| 584 | 577 | const value = switch (node.get(self.file.zoir.?)) { |
| 585 | 578 | .int_literal => |int| switch (int) { |
| 586 | 579 | .small => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))), |
| 587 | .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128)), | |
| 580 | .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128, .nearest_even)[0]), | |
| 588 | 581 | }, |
| 589 | 582 | .float_literal => |val| try self.sema.pt.floatValue(res_ty, val), |
| 590 | 583 | .char_literal => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))), |
src/Value.zig+5-19| ... | ... | @@ -898,7 +898,7 @@ pub fn readFromPackedMemory( |
| 898 | 898 | pub fn toFloat(val: Value, comptime T: type, zcu: *const Zcu) T { |
| 899 | 899 | return switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 900 | 900 | .int => |int| switch (int.storage) { |
| 901 | .big_int => |big_int| big_int.toFloat(T), | |
| 901 | .big_int => |big_int| big_int.toFloat(T, .nearest_even)[0], | |
| 902 | 902 | inline .u64, .i64 => |x| { |
| 903 | 903 | if (T == f80) { |
| 904 | 904 | @panic("TODO we can't lower this properly on non-x86 llvm backend yet"); |
| ... | ... | @@ -997,16 +997,6 @@ pub fn floatCast(val: Value, dest_ty: Type, pt: Zcu.PerThread) !Value { |
| 997 | 997 | } })); |
| 998 | 998 | } |
| 999 | 999 | |
| 1000 | /// Asserts the value is a float | |
| 1001 | pub fn floatHasFraction(self: Value, zcu: *const Zcu) bool { | |
| 1002 | return switch (zcu.intern_pool.indexToKey(self.toIntern())) { | |
| 1003 | .float => |float| switch (float.storage) { | |
| 1004 | inline else => |x| @rem(x, 1) != 0, | |
| 1005 | }, | |
| 1006 | else => unreachable, | |
| 1007 | }; | |
| 1008 | } | |
| 1009 | ||
| 1010 | 1000 | pub fn orderAgainstZero(lhs: Value, zcu: *Zcu) std.math.Order { |
| 1011 | 1001 | return orderAgainstZeroInner(lhs, .normal, zcu, {}) catch unreachable; |
| 1012 | 1002 | } |
| ... | ... | @@ -1557,17 +1547,13 @@ pub fn floatFromIntAdvanced( |
| 1557 | 1547 | } |
| 1558 | 1548 | |
| 1559 | 1549 | pub fn floatFromIntScalar(val: Value, float_ty: Type, pt: Zcu.PerThread, comptime strat: ResolveStrat) !Value { |
| 1560 | const zcu = pt.zcu; | |
| 1561 | return switch (zcu.intern_pool.indexToKey(val.toIntern())) { | |
| 1550 | return switch (pt.zcu.intern_pool.indexToKey(val.toIntern())) { | |
| 1562 | 1551 | .undef => try pt.undefValue(float_ty), |
| 1563 | 1552 | .int => |int| switch (int.storage) { |
| 1564 | .big_int => |big_int| { | |
| 1565 | const float = big_int.toFloat(f128); | |
| 1566 | return pt.floatValue(float_ty, float); | |
| 1567 | }, | |
| 1553 | .big_int => |big_int| pt.floatValue(float_ty, big_int.toFloat(f128, .nearest_even)[0]), | |
| 1568 | 1554 | inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, pt), |
| 1569 | .lazy_align => |ty| return floatFromIntInner((try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), pt.zcu, pt.tid)).scalar.toByteUnits() orelse 0, float_ty, pt), | |
| 1570 | .lazy_size => |ty| return floatFromIntInner((try Type.fromInterned(ty).abiSizeInner(strat.toLazy(), pt.zcu, pt.tid)).scalar, float_ty, pt), | |
| 1555 | .lazy_align => |ty| floatFromIntInner((try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), pt.zcu, pt.tid)).scalar.toByteUnits() orelse 0, float_ty, pt), | |
| 1556 | .lazy_size => |ty| floatFromIntInner((try Type.fromInterned(ty).abiSizeInner(strat.toLazy(), pt.zcu, pt.tid)).scalar, float_ty, pt), | |
| 1571 | 1557 | }, |
| 1572 | 1558 | else => unreachable, |
| 1573 | 1559 | }; |