| author | |
| committer | |
| log | c5b96c7447efde0d10de9689f03d151afcafbad5 |
| tree | 5defce62f9cca4ee233197487c786ac197a7fb75 |
| parent | 2d2d79a05b8423c7638348dc5a89793c1e0cafce |
Closes #156116 files changed, 55 insertions(+), 23 deletions(-)
lib/std/math.zig+1| ... | @@ -37,6 +37,7 @@ pub const sqrt2 = 1.414213562373095048801688724209698079; | ... | @@ -37,6 +37,7 @@ pub const sqrt2 = 1.414213562373095048801688724209698079; |
| 37 | /// 1/sqrt(2) | 37 | /// 1/sqrt(2) |
| 38 | pub const sqrt1_2 = 0.707106781186547524400844362104849039; | 38 | pub const sqrt1_2 = 0.707106781186547524400844362104849039; |
| 39 | 39 | ||
| 40 | pub const floatBits = @import("math/float.zig").floatBits; | ||
| 40 | pub const floatExponentBits = @import("math/float.zig").floatExponentBits; | 41 | pub const floatExponentBits = @import("math/float.zig").floatExponentBits; |
| 41 | pub const floatMantissaBits = @import("math/float.zig").floatMantissaBits; | 42 | pub const floatMantissaBits = @import("math/float.zig").floatMantissaBits; |
| 42 | pub const floatFractionalBits = @import("math/float.zig").floatFractionalBits; | 43 | pub const floatFractionalBits = @import("math/float.zig").floatFractionalBits; |
lib/std/math/copysign.zig+8-7| ... | @@ -4,16 +4,17 @@ const expect = std.testing.expect; | ... | @@ -4,16 +4,17 @@ const expect = std.testing.expect; |
| 4 | 4 | ||
| 5 | /// Returns a value with the magnitude of `magnitude` and the sign of `sign`. | 5 | /// Returns a value with the magnitude of `magnitude` and the sign of `sign`. |
| 6 | pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude) { | 6 | pub fn copysign(magnitude: anytype, sign: @TypeOf(magnitude)) @TypeOf(magnitude) { |
| 7 | const T = @TypeOf(magnitude); | 7 | const bits = math.floatBits(@TypeOf(magnitude)); |
| 8 | const TBits = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | 8 | const FBits = @Type(.{ .Float = .{ .bits = bits } }); |
| 9 | const sign_bit_mask = @as(TBits, 1) << (@bitSizeOf(T) - 1); | 9 | const TBits = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = bits } }); |
| 10 | const mag = @bitCast(TBits, magnitude) & ~sign_bit_mask; | 10 | const sign_bit_mask = @as(TBits, 1) << (bits - 1); |
| 11 | const sgn = @bitCast(TBits, sign) & sign_bit_mask; | 11 | const mag = @bitCast(TBits, @as(FBits, magnitude)) & ~sign_bit_mask; |
| 12 | return @bitCast(T, mag | sgn); | 12 | const sgn = @bitCast(TBits, @as(FBits, sign)) & sign_bit_mask; |
| 13 | return @bitCast(FBits, mag | sgn); | ||
| 13 | } | 14 | } |
| 14 | 15 | ||
| 15 | test "math.copysign" { | 16 | test "math.copysign" { |
| 16 | inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| { | 17 | inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble, comptime_float }) |T| { |
| 17 | try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0); | 18 | try expect(copysign(@as(T, 1.0), @as(T, 1.0)) == 1.0); |
| 18 | try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0); | 19 | try expect(copysign(@as(T, 2.0), @as(T, -2.0)) == -2.0); |
| 19 | try expect(copysign(@as(T, -3.0), @as(T, 3.0)) == 3.0); | 20 | try expect(copysign(@as(T, -3.0), @as(T, 3.0)) == 3.0); |
lib/std/math/float.zig+17-12| ... | @@ -4,21 +4,29 @@ const expect = std.testing.expect; | ... | @@ -4,21 +4,29 @@ const expect = std.testing.expect; |
| 4 | 4 | ||
| 5 | /// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic. | 5 | /// Creates a raw "1.0" mantissa for floating point type T. Used to dedupe f80 logic. |
| 6 | inline fn mantissaOne(comptime T: type) comptime_int { | 6 | inline fn mantissaOne(comptime T: type) comptime_int { |
| 7 | return if (@typeInfo(T).Float.bits == 80) 1 << floatFractionalBits(T) else 0; | 7 | return 1 << floatFractionalBits(T) & ((1 << floatMantissaBits(T)) - 1); |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | /// Creates floating point type T from an unbiased exponent and raw mantissa. | 10 | /// Creates floating point type T from an unbiased exponent and raw mantissa. |
| 11 | inline fn reconstructFloat(comptime T: type, comptime exponent: comptime_int, comptime mantissa: comptime_int) T { | 11 | inline fn reconstructFloat(comptime T: type, comptime exponent: comptime_int, comptime mantissa: comptime_int) T { |
| 12 | const TBits = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = @bitSizeOf(T) } }); | 12 | const FBits = @Type(.{ .Float = .{ .bits = floatBits(T) } }); |
| 13 | const TBits = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = floatBits(T) } }); | ||
| 13 | const biased_exponent = @as(TBits, exponent + floatExponentMax(T)); | 14 | const biased_exponent = @as(TBits, exponent + floatExponentMax(T)); |
| 14 | return @bitCast(T, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa)); | 15 | return @bitCast(FBits, (biased_exponent << floatMantissaBits(T)) | @as(TBits, mantissa)); |
| 16 | } | ||
| 17 | |||
| 18 | /// Returns the number of bits in floating point type T. | ||
| 19 | pub inline fn floatBits(comptime T: type) comptime_int { | ||
| 20 | return switch (@typeInfo(T)) { | ||
| 21 | .Float => |info| info.bits, | ||
| 22 | .ComptimeFloat => 128, | ||
| 23 | else => @compileError(@typeName(T) ++ " is not a floating point type"), | ||
| 24 | }; | ||
| 15 | } | 25 | } |
| 16 | 26 | ||
| 17 | /// Returns the number of bits in the exponent of floating point type T. | 27 | /// Returns the number of bits in the exponent of floating point type T. |
| 18 | pub inline fn floatExponentBits(comptime T: type) comptime_int { | 28 | pub inline fn floatExponentBits(comptime T: type) comptime_int { |
| 19 | comptime assert(@typeInfo(T) == .Float); | 29 | return switch (floatBits(T)) { |
| 20 | |||
| 21 | return switch (@typeInfo(T).Float.bits) { | ||
| 22 | 16 => 5, | 30 | 16 => 5, |
| 23 | 32 => 8, | 31 | 32 => 8, |
| 24 | 64 => 11, | 32 | 64 => 11, |
| ... | @@ -30,9 +38,7 @@ pub inline fn floatExponentBits(comptime T: type) comptime_int { | ... | @@ -30,9 +38,7 @@ pub inline fn floatExponentBits(comptime T: type) comptime_int { |
| 30 | 38 | ||
| 31 | /// Returns the number of bits in the mantissa of floating point type T. | 39 | /// Returns the number of bits in the mantissa of floating point type T. |
| 32 | pub inline fn floatMantissaBits(comptime T: type) comptime_int { | 40 | pub inline fn floatMantissaBits(comptime T: type) comptime_int { |
| 33 | comptime assert(@typeInfo(T) == .Float); | 41 | return switch (floatBits(T)) { |
| 34 | |||
| 35 | return switch (@typeInfo(T).Float.bits) { | ||
| 36 | 16 => 10, | 42 | 16 => 10, |
| 37 | 32 => 23, | 43 | 32 => 23, |
| 38 | 64 => 52, | 44 | 64 => 52, |
| ... | @@ -44,12 +50,10 @@ pub inline fn floatMantissaBits(comptime T: type) comptime_int { | ... | @@ -44,12 +50,10 @@ pub inline fn floatMantissaBits(comptime T: type) comptime_int { |
| 44 | 50 | ||
| 45 | /// Returns the number of fractional bits in the mantissa of floating point type T. | 51 | /// Returns the number of fractional bits in the mantissa of floating point type T. |
| 46 | pub inline fn floatFractionalBits(comptime T: type) comptime_int { | 52 | pub inline fn floatFractionalBits(comptime T: type) comptime_int { |
| 47 | comptime assert(@typeInfo(T) == .Float); | ||
| 48 | |||
| 49 | // standard IEEE floats have an implicit 0.m or 1.m integer part | 53 | // standard IEEE floats have an implicit 0.m or 1.m integer part |
| 50 | // f80 is special and has an explicitly stored bit in the MSB | 54 | // f80 is special and has an explicitly stored bit in the MSB |
| 51 | // this function corresponds to `MANT_DIG - 1' from C | 55 | // this function corresponds to `MANT_DIG - 1' from C |
| 52 | return switch (@typeInfo(T).Float.bits) { | 56 | return switch (floatBits(T)) { |
| 53 | 16 => 10, | 57 | 16 => 10, |
| 54 | 32 => 23, | 58 | 32 => 23, |
| 55 | 64 => 52, | 59 | 64 => 52, |
| ... | @@ -101,6 +105,7 @@ test "float bits" { | ... | @@ -101,6 +105,7 @@ test "float bits" { |
| 101 | inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { | 105 | inline for ([_]type{ f16, f32, f64, f80, f128, c_longdouble }) |T| { |
| 102 | // (1 +) for the sign bit, since it is separate from the other bits | 106 | // (1 +) for the sign bit, since it is separate from the other bits |
| 103 | const size = 1 + floatExponentBits(T) + floatMantissaBits(T); | 107 | const size = 1 + floatExponentBits(T) + floatMantissaBits(T); |
| 108 | try expect(floatBits(T) == size); | ||
| 104 | try expect(@bitSizeOf(T) == size); | 109 | try expect(@bitSizeOf(T) == size); |
| 105 | 110 | ||
| 106 | // for machine epsilon, assert expmin <= -prec <= expmax | 111 | // for machine epsilon, assert expmin <= -prec <= expmax |
lib/std/math/nan.zig+2-2| ... | @@ -2,13 +2,13 @@ const math = @import("../math.zig"); | ... | @@ -2,13 +2,13 @@ const math = @import("../math.zig"); |
| 2 | 2 | ||
| 3 | /// Returns the nan representation for type T. | 3 | /// Returns the nan representation for type T. |
| 4 | pub inline fn nan(comptime T: type) T { | 4 | pub inline fn nan(comptime T: type) T { |
| 5 | return switch (@typeInfo(T).Float.bits) { | 5 | return switch (math.floatBits(T)) { |
| 6 | 16 => math.nan_f16, | 6 | 16 => math.nan_f16, |
| 7 | 32 => math.nan_f32, | 7 | 32 => math.nan_f32, |
| 8 | 64 => math.nan_f64, | 8 | 64 => math.nan_f64, |
| 9 | 80 => math.nan_f80, | 9 | 80 => math.nan_f80, |
| 10 | 128 => math.nan_f128, | 10 | 128 => math.nan_f128, |
| 11 | else => @compileError("unreachable"), | 11 | else => @compileError("unknown floating point type " ++ @typeName(T)), |
| 12 | }; | 12 | }; |
| 13 | } | 13 | } |
| 14 | 14 |
src/codegen/llvm.zig+2-2| ... | @@ -7034,7 +7034,7 @@ pub const FuncGen = struct { | ... | @@ -7034,7 +7034,7 @@ pub const FuncGen = struct { |
| 7034 | const rhs = try self.resolveInst(bin_op.rhs); | 7034 | const rhs = try self.resolveInst(bin_op.rhs); |
| 7035 | const scalar_ty = self.air.typeOfIndex(inst).scalarType(); | 7035 | const scalar_ty = self.air.typeOfIndex(inst).scalarType(); |
| 7036 | 7036 | ||
| 7037 | if (scalar_ty.isAnyFloat()) return self.builder.buildMinNum(lhs, rhs, ""); | 7037 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.fmin, scalar_ty, 2, .{ lhs, rhs }); |
| 7038 | if (scalar_ty.isSignedInt()) return self.builder.buildSMin(lhs, rhs, ""); | 7038 | if (scalar_ty.isSignedInt()) return self.builder.buildSMin(lhs, rhs, ""); |
| 7039 | return self.builder.buildUMin(lhs, rhs, ""); | 7039 | return self.builder.buildUMin(lhs, rhs, ""); |
| 7040 | } | 7040 | } |
| ... | @@ -7045,7 +7045,7 @@ pub const FuncGen = struct { | ... | @@ -7045,7 +7045,7 @@ pub const FuncGen = struct { |
| 7045 | const rhs = try self.resolveInst(bin_op.rhs); | 7045 | const rhs = try self.resolveInst(bin_op.rhs); |
| 7046 | const scalar_ty = self.air.typeOfIndex(inst).scalarType(); | 7046 | const scalar_ty = self.air.typeOfIndex(inst).scalarType(); |
| 7047 | 7047 | ||
| 7048 | if (scalar_ty.isAnyFloat()) return self.builder.buildMaxNum(lhs, rhs, ""); | 7048 | if (scalar_ty.isAnyFloat()) return self.buildFloatOp(.fmax, scalar_ty, 2, .{ lhs, rhs }); |
| 7049 | if (scalar_ty.isSignedInt()) return self.builder.buildSMax(lhs, rhs, ""); | 7049 | if (scalar_ty.isSignedInt()) return self.builder.buildSMax(lhs, rhs, ""); |
| 7050 | return self.builder.buildUMax(lhs, rhs, ""); | 7050 | return self.builder.buildUMax(lhs, rhs, ""); |
| 7051 | } | 7051 | } |
test/behavior/maximum_minimum.zig+25| ... | @@ -96,6 +96,31 @@ test "@min for vectors" { | ... | @@ -96,6 +96,31 @@ test "@min for vectors" { |
| 96 | comptime try S.doTheTest(); | 96 | comptime try S.doTheTest(); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | test "@min/max for floats" { | ||
| 100 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 101 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO | ||
| 102 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | ||
| 103 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | ||
| 104 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | ||
| 105 | |||
| 106 | const S = struct { | ||
| 107 | fn doTheTest(comptime T: type) !void { | ||
| 108 | var x: T = -3.14; | ||
| 109 | var y: T = 5.27; | ||
| 110 | try expectEqual(x, @min(x, y)); | ||
| 111 | try expectEqual(x, @min(y, x)); | ||
| 112 | try expectEqual(y, @max(x, y)); | ||
| 113 | try expectEqual(y, @max(y, x)); | ||
| 114 | } | ||
| 115 | }; | ||
| 116 | |||
| 117 | inline for (.{ f16, f32, f64, f80, f128, c_longdouble }) |T| { | ||
| 118 | try S.doTheTest(T); | ||
| 119 | comptime try S.doTheTest(T); | ||
| 120 | } | ||
| 121 | comptime try S.doTheTest(comptime_float); | ||
| 122 | } | ||
| 123 | |||
| 99 | test "@min/@max on lazy values" { | 124 | test "@min/@max on lazy values" { |
| 100 | const A = extern struct { u8_4: [4]u8 }; | 125 | const A = extern struct { u8_4: [4]u8 }; |
| 101 | const B = extern struct { u8_16: [16]u8 }; | 126 | const B = extern struct { u8_16: [16]u8 }; |