authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-15 13:21:52-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-04-15 13:26:58-04:00
logb5459eb987d89c4759c31123a7baa0a0d962c024
tree4a825823adb391e54c48f7664579ad085c75724b
parent4a2bfec150ac8b78185d98324782da7841eddb9b

add @sqrt built-in function

See #767

13 files changed, 419 insertions(+), 288 deletions(-)

CMakeLists.txt-1
...@@ -498,7 +498,6 @@ set(ZIG_STD_FILES...@@ -498,7 +498,6 @@ set(ZIG_STD_FILES
498 "math/tan.zig"498 "math/tan.zig"
499 "math/tanh.zig"499 "math/tanh.zig"
500 "math/trunc.zig"500 "math/trunc.zig"
501 "math/x86_64/sqrt.zig"
502 "mem.zig"501 "mem.zig"
503 "net.zig"502 "net.zig"
504 "os/child_process.zig"503 "os/child_process.zig"
doc/langref.html.in+11-1
...@@ -4669,6 +4669,16 @@ pub const FloatMode = enum {...@@ -4669,6 +4669,16 @@ pub const FloatMode = enum {
4669 The result is a target-specific compile time constant.4669 The result is a target-specific compile time constant.
4670 </p>4670 </p>
4671 {#header_close#}4671 {#header_close#}
4672 {#header_open|@sqrt#}
4673 <pre><code class="zig">@sqrt(comptime T: type, value: T) -&gt; T</code></pre>
4674 <p>
4675 Performs the square root of a floating point number. Uses a dedicated hardware instruction
4676 when available. Currently only supports f32 and f64 at runtime. f128 at runtime is TODO.
4677 </p>
4678 <p>
4679 This is a low-level intrinsic. Most code can use <code>std.math.sqrt</code> instead.
4680 </p>
4681 {#header_close#}
4672 {#header_open|@subWithOverflow#}4682 {#header_open|@subWithOverflow#}
4673 <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -&gt; bool</code></pre>4683 <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -&gt; bool</code></pre>
4674 <p>4684 <p>
...@@ -5991,7 +6001,7 @@ hljs.registerLanguage("zig", function(t) {...@@ -5991,7 +6001,7 @@ hljs.registerLanguage("zig", function(t) {
5991 a = t.IR + "\\s*\\(",6001 a = t.IR + "\\s*\\(",
5992 c = {6002 c = {
5993 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",6003 keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong",
5994 built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw",6004 built_in: "breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt",
5995 literal: "true false null undefined"6005 literal: "true false null undefined"
5996 },6006 },
5997 n = [e, t.CLCM, t.CBCM, s, r];6007 n = [e, t.CLCM, t.CBCM, s, r];
src/all_types.hpp+11-1
...@@ -1317,6 +1317,7 @@ enum BuiltinFnId {...@@ -1317,6 +1317,7 @@ enum BuiltinFnId {
1317 BuiltinFnIdDivFloor,1317 BuiltinFnIdDivFloor,
1318 BuiltinFnIdRem,1318 BuiltinFnIdRem,
1319 BuiltinFnIdMod,1319 BuiltinFnIdMod,
1320 BuiltinFnIdSqrt,
1320 BuiltinFnIdTruncate,1321 BuiltinFnIdTruncate,
1321 BuiltinFnIdIntType,1322 BuiltinFnIdIntType,
1322 BuiltinFnIdSetCold,1323 BuiltinFnIdSetCold,
...@@ -1413,6 +1414,7 @@ enum ZigLLVMFnId {...@@ -1413,6 +1414,7 @@ enum ZigLLVMFnId {
1413 ZigLLVMFnIdOverflowArithmetic,1414 ZigLLVMFnIdOverflowArithmetic,
1414 ZigLLVMFnIdFloor,1415 ZigLLVMFnIdFloor,
1415 ZigLLVMFnIdCeil,1416 ZigLLVMFnIdCeil,
1417 ZigLLVMFnIdSqrt,
1416};1418};
14171419
1418enum AddSubMul {1420enum AddSubMul {
...@@ -1433,7 +1435,7 @@ struct ZigLLVMFnKey {...@@ -1433,7 +1435,7 @@ struct ZigLLVMFnKey {
1433 } clz;1435 } clz;
1434 struct {1436 struct {
1435 uint32_t bit_count;1437 uint32_t bit_count;
1436 } floor_ceil;1438 } floating;
1437 struct {1439 struct {
1438 AddSubMul add_sub_mul;1440 AddSubMul add_sub_mul;
1439 uint32_t bit_count;1441 uint32_t bit_count;
...@@ -2047,6 +2049,7 @@ enum IrInstructionId {...@@ -2047,6 +2049,7 @@ enum IrInstructionId {
2047 IrInstructionIdAddImplicitReturnType,2049 IrInstructionIdAddImplicitReturnType,
2048 IrInstructionIdMergeErrRetTraces,2050 IrInstructionIdMergeErrRetTraces,
2049 IrInstructionIdMarkErrRetTracePtr,2051 IrInstructionIdMarkErrRetTracePtr,
2052 IrInstructionIdSqrt,
2050};2053};
20512054
2052struct IrInstruction {2055struct IrInstruction {
...@@ -3036,6 +3039,13 @@ struct IrInstructionMarkErrRetTracePtr {...@@ -3036,6 +3039,13 @@ struct IrInstructionMarkErrRetTracePtr {
3036 IrInstruction *err_ret_trace_ptr;3039 IrInstruction *err_ret_trace_ptr;
3037};3040};
30383041
3042struct IrInstructionSqrt {
3043 IrInstruction base;
3044
3045 IrInstruction *type;
3046 IrInstruction *op;
3047};
3048
3039static const size_t slice_ptr_index = 0;3049static const size_t slice_ptr_index = 0;
3040static const size_t slice_len_index = 1;3050static const size_t slice_len_index = 1;
30413051
src/analyze.cpp+6-3
...@@ -5801,9 +5801,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {...@@ -5801,9 +5801,11 @@ uint32_t zig_llvm_fn_key_hash(ZigLLVMFnKey x) {
5801 case ZigLLVMFnIdClz:5801 case ZigLLVMFnIdClz:
5802 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;5802 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
5803 case ZigLLVMFnIdFloor:5803 case ZigLLVMFnIdFloor:
5804 return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1899859168;5804 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1899859168;
5805 case ZigLLVMFnIdCeil:5805 case ZigLLVMFnIdCeil:
5806 return (uint32_t)(x.data.floor_ceil.bit_count) * (uint32_t)1953839089;5806 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)1953839089;
5807 case ZigLLVMFnIdSqrt:
5808 return (uint32_t)(x.data.floating.bit_count) * (uint32_t)2225366385;
5807 case ZigLLVMFnIdOverflowArithmetic:5809 case ZigLLVMFnIdOverflowArithmetic:
5808 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +5810 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
5809 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +5811 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
...@@ -5822,7 +5824,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {...@@ -5822,7 +5824,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
5822 return a.data.clz.bit_count == b.data.clz.bit_count;5824 return a.data.clz.bit_count == b.data.clz.bit_count;
5823 case ZigLLVMFnIdFloor:5825 case ZigLLVMFnIdFloor:
5824 case ZigLLVMFnIdCeil:5826 case ZigLLVMFnIdCeil:
5825 return a.data.floor_ceil.bit_count == b.data.floor_ceil.bit_count;5827 case ZigLLVMFnIdSqrt:
5828 return a.data.floating.bit_count == b.data.floating.bit_count;
5826 case ZigLLVMFnIdOverflowArithmetic:5829 case ZigLLVMFnIdOverflowArithmetic:
5827 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&5830 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
5828 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&5831 (a.data.overflow_arithmetic.add_sub_mul == b.data.overflow_arithmetic.add_sub_mul) &&
src/bigfloat.cpp+4
...@@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {...@@ -181,3 +181,7 @@ bool bigfloat_has_fraction(const BigFloat *bigfloat) {
181 f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);181 f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
182 return !f128M_eq(&floored, &bigfloat->value);182 return !f128M_eq(&floored, &bigfloat->value);
183}183}
184
185void bigfloat_sqrt(BigFloat *dest, const BigFloat *op) {
186 f128M_sqrt(&op->value, &dest->value);
187}
src/bigfloat.hpp+1
...@@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2...@@ -42,6 +42,7 @@ void bigfloat_div_trunc(BigFloat *dest, const BigFloat *op1, const BigFloat *op2
42void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);42void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
43void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);43void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
44void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);44void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
45void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
45void bigfloat_append_buf(Buf *buf, const BigFloat *op);46void bigfloat_append_buf(Buf *buf, const BigFloat *op);
46Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);47Cmp bigfloat_cmp(const BigFloat *op1, const BigFloat *op2);
4748
src/codegen.cpp+19-5
...@@ -717,12 +717,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,...@@ -717,12 +717,12 @@ static LLVMValueRef get_int_overflow_fn(CodeGen *g, TypeTableEntry *type_entry,
717 return fn_val;717 return fn_val;
718}718}
719719
720static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {720static LLVMValueRef get_float_fn(CodeGen *g, TypeTableEntry *type_entry, ZigLLVMFnId fn_id) {
721 assert(type_entry->id == TypeTableEntryIdFloat);721 assert(type_entry->id == TypeTableEntryIdFloat);
722722
723 ZigLLVMFnKey key = {};723 ZigLLVMFnKey key = {};
724 key.id = fn_id;724 key.id = fn_id;
725 key.data.floor_ceil.bit_count = (uint32_t)type_entry->data.floating.bit_count;725 key.data.floating.bit_count = (uint32_t)type_entry->data.floating.bit_count;
726726
727 auto existing_entry = g->llvm_fn_table.maybe_get(key);727 auto existing_entry = g->llvm_fn_table.maybe_get(key);
728 if (existing_entry)728 if (existing_entry)
...@@ -733,6 +733,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi...@@ -733,6 +733,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi
733 name = "floor";733 name = "floor";
734 } else if (fn_id == ZigLLVMFnIdCeil) {734 } else if (fn_id == ZigLLVMFnIdCeil) {
735 name = "ceil";735 name = "ceil";
736 } else if (fn_id == ZigLLVMFnIdSqrt) {
737 name = "sqrt";
736 } else {738 } else {
737 zig_unreachable();739 zig_unreachable();
738 }740 }
...@@ -1900,7 +1902,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type...@@ -1900,7 +1902,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type
1900 if (type_entry->id == TypeTableEntryIdInt)1902 if (type_entry->id == TypeTableEntryIdInt)
1901 return val;1903 return val;
19021904
1903 LLVMValueRef floor_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdFloor);1905 LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloor);
1904 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");1906 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
1905}1907}
19061908
...@@ -1908,7 +1910,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_...@@ -1908,7 +1910,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_
1908 if (type_entry->id == TypeTableEntryIdInt)1910 if (type_entry->id == TypeTableEntryIdInt)
1909 return val;1911 return val;
19101912
1911 LLVMValueRef ceil_fn = get_floor_ceil_fn(g, type_entry, ZigLLVMFnIdCeil);1913 LLVMValueRef ceil_fn = get_float_fn(g, type_entry, ZigLLVMFnIdCeil);
1912 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");1914 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
1913}1915}
19141916
...@@ -3247,10 +3249,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui...@@ -3247,10 +3249,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui
3247 fn_name = "cttz";3249 fn_name = "cttz";
3248 key.id = ZigLLVMFnIdCtz;3250 key.id = ZigLLVMFnIdCtz;
3249 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;3251 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3250 } else {3252 } else if (fn_id == BuiltinFnIdClz) {
3251 fn_name = "ctlz";3253 fn_name = "ctlz";
3252 key.id = ZigLLVMFnIdClz;3254 key.id = ZigLLVMFnIdClz;
3253 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;3255 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3256 } else {
3257 zig_unreachable();
3254 }3258 }
32553259
3256 auto existing_entry = g->llvm_fn_table.maybe_get(key);3260 auto existing_entry = g->llvm_fn_table.maybe_get(key);
...@@ -4402,6 +4406,13 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e...@@ -4402,6 +4406,13 @@ static LLVMValueRef ir_render_mark_err_ret_trace_ptr(CodeGen *g, IrExecutable *e
4402 return nullptr;4406 return nullptr;
4403}4407}
44044408
4409static LLVMValueRef ir_render_sqrt(CodeGen *g, IrExecutable *executable, IrInstructionSqrt *instruction) {
4410 LLVMValueRef op = ir_llvm_value(g, instruction->op);
4411 assert(instruction->base.value.type->id == TypeTableEntryIdFloat);
4412 LLVMValueRef fn_val = get_float_fn(g, instruction->base.value.type, ZigLLVMFnIdSqrt);
4413 return LLVMBuildCall(g->builder, fn_val, &op, 1, "");
4414}
4415
4405static void set_debug_location(CodeGen *g, IrInstruction *instruction) {4416static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
4406 AstNode *source_node = instruction->source_node;4417 AstNode *source_node = instruction->source_node;
4407 Scope *scope = instruction->scope;4418 Scope *scope = instruction->scope;
...@@ -4623,6 +4634,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,...@@ -4623,6 +4634,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
4623 return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);4634 return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
4624 case IrInstructionIdMarkErrRetTracePtr:4635 case IrInstructionIdMarkErrRetTracePtr:
4625 return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);4636 return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
4637 case IrInstructionIdSqrt:
4638 return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
4626 }4639 }
4627 zig_unreachable();4640 zig_unreachable();
4628}4641}
...@@ -6109,6 +6122,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -6109,6 +6122,7 @@ static void define_builtin_fns(CodeGen *g) {
6109 create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);6122 create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);
6110 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);6123 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
6111 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);6124 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
6125 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
6112 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);6126 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
6113 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);6127 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
6114 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);6128 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
src/ir.cpp+94
...@@ -733,6 +733,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP...@@ -733,6 +733,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP
733 return IrInstructionIdMarkErrRetTracePtr;733 return IrInstructionIdMarkErrRetTracePtr;
734}734}
735735
736static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
737 return IrInstructionIdSqrt;
738}
739
736template<typename T>740template<typename T>
737static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {741static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
738 T *special_instruction = allocate<T>(1);742 T *special_instruction = allocate<T>(1);
...@@ -2731,6 +2735,17 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco...@@ -2731,6 +2735,17 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco
2731 return &instruction->base;2735 return &instruction->base;
2732}2736}
27332737
2738static IrInstruction *ir_build_sqrt(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type, IrInstruction *op) {
2739 IrInstructionSqrt *instruction = ir_build_instruction<IrInstructionSqrt>(irb, scope, source_node);
2740 instruction->type = type;
2741 instruction->op = op;
2742
2743 if (type != nullptr) ir_ref_instruction(type, irb->current_basic_block);
2744 ir_ref_instruction(op, irb->current_basic_block);
2745
2746 return &instruction->base;
2747}
2748
2734static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {2749static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
2735 results[ReturnKindUnconditional] = 0;2750 results[ReturnKindUnconditional] = 0;
2736 results[ReturnKindError] = 0;2751 results[ReturnKindError] = 0;
...@@ -3845,6 +3860,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -3845,6 +3860,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
38453860
3846 return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);3861 return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
3847 }3862 }
3863 case BuiltinFnIdSqrt:
3864 {
3865 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
3866 IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
3867 if (arg0_value == irb->codegen->invalid_instruction)
3868 return arg0_value;
3869
3870 AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
3871 IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
3872 if (arg1_value == irb->codegen->invalid_instruction)
3873 return arg1_value;
3874
3875 return ir_build_sqrt(irb, scope, node, arg0_value, arg1_value);
3876 }
3848 case BuiltinFnIdTruncate:3877 case BuiltinFnIdTruncate:
3849 {3878 {
3850 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);3879 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
...@@ -18031,6 +18060,68 @@ static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *...@@ -18031,6 +18060,68 @@ static TypeTableEntry *ir_analyze_instruction_mark_err_ret_trace_ptr(IrAnalyze *
18031 return result->value.type;18060 return result->value.type;
18032}18061}
1803318062
18063static TypeTableEntry *ir_analyze_instruction_sqrt(IrAnalyze *ira, IrInstructionSqrt *instruction) {
18064 TypeTableEntry *float_type = ir_resolve_type(ira, instruction->type->other);
18065 if (type_is_invalid(float_type))
18066 return ira->codegen->builtin_types.entry_invalid;
18067
18068 IrInstruction *op = instruction->op->other;
18069 if (type_is_invalid(op->value.type))
18070 return ira->codegen->builtin_types.entry_invalid;
18071
18072 bool ok_type = float_type->id == TypeTableEntryIdNumLitFloat || float_type->id == TypeTableEntryIdFloat;
18073 if (!ok_type) {
18074 ir_add_error(ira, instruction->type, buf_sprintf("@sqrt does not support type '%s'", buf_ptr(&float_type->name)));
18075 return ira->codegen->builtin_types.entry_invalid;
18076 }
18077
18078 IrInstruction *casted_op = ir_implicit_cast(ira, op, float_type);
18079 if (type_is_invalid(casted_op->value.type))
18080 return ira->codegen->builtin_types.entry_invalid;
18081
18082 if (instr_is_comptime(casted_op)) {
18083 ConstExprValue *val = ir_resolve_const(ira, casted_op, UndefBad);
18084 if (!val)
18085 return ira->codegen->builtin_types.entry_invalid;
18086
18087 ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
18088
18089 if (float_type->id == TypeTableEntryIdNumLitFloat) {
18090 bigfloat_sqrt(&out_val->data.x_bigfloat, &val->data.x_bigfloat);
18091 } else if (float_type->id == TypeTableEntryIdFloat) {
18092 switch (float_type->data.floating.bit_count) {
18093 case 32:
18094 out_val->data.x_f32 = sqrtf(val->data.x_f32);
18095 break;
18096 case 64:
18097 out_val->data.x_f64 = sqrt(val->data.x_f64);
18098 break;
18099 case 128:
18100 f128M_sqrt(&val->data.x_f128, &out_val->data.x_f128);
18101 break;
18102 default:
18103 zig_unreachable();
18104 }
18105 } else {
18106 zig_unreachable();
18107 }
18108
18109 return float_type;
18110 }
18111
18112 assert(float_type->id == TypeTableEntryIdFloat);
18113 if (float_type->data.floating.bit_count != 32 && float_type->data.floating.bit_count != 64) {
18114 ir_add_error(ira, instruction->type, buf_sprintf("compiler TODO: add implementation of sqrt for '%s'", buf_ptr(&float_type->name)));
18115 return ira->codegen->builtin_types.entry_invalid;
18116 }
18117
18118 IrInstruction *result = ir_build_sqrt(&ira->new_irb, instruction->base.scope,
18119 instruction->base.source_node, nullptr, casted_op);
18120 ir_link_new_instruction(result, &instruction->base);
18121 result->value.type = float_type;
18122 return result->value.type;
18123}
18124
18034static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {18125static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
18035 switch (instruction->id) {18126 switch (instruction->id) {
18036 case IrInstructionIdInvalid:18127 case IrInstructionIdInvalid:
...@@ -18278,6 +18369,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi...@@ -18278,6 +18369,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
18278 return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);18369 return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
18279 case IrInstructionIdMarkErrRetTracePtr:18370 case IrInstructionIdMarkErrRetTracePtr:
18280 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);18371 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
18372 case IrInstructionIdSqrt:
18373 return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
18281 }18374 }
18282 zig_unreachable();18375 zig_unreachable();
18283}18376}
...@@ -18490,6 +18583,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {...@@ -18490,6 +18583,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
18490 case IrInstructionIdCoroFree:18583 case IrInstructionIdCoroFree:
18491 case IrInstructionIdCoroPromise:18584 case IrInstructionIdCoroPromise:
18492 case IrInstructionIdPromiseResultType:18585 case IrInstructionIdPromiseResultType:
18586 case IrInstructionIdSqrt:
18493 return false;18587 return false;
1849418588
18495 case IrInstructionIdAsm:18589 case IrInstructionIdAsm:
src/ir_print.cpp+15
...@@ -1204,6 +1204,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe...@@ -1204,6 +1204,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe
1204 fprintf(irp->f, ")");1204 fprintf(irp->f, ")");
1205}1205}
12061206
1207static void ir_print_sqrt(IrPrint *irp, IrInstructionSqrt *instruction) {
1208 fprintf(irp->f, "@sqrt(");
1209 if (instruction->type != nullptr) {
1210 ir_print_other_instruction(irp, instruction->type);
1211 } else {
1212 fprintf(irp->f, "null");
1213 }
1214 fprintf(irp->f, ",");
1215 ir_print_other_instruction(irp, instruction->op);
1216 fprintf(irp->f, ")");
1217}
1218
1207static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {1219static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1208 ir_print_prefix(irp, instruction);1220 ir_print_prefix(irp, instruction);
1209 switch (instruction->id) {1221 switch (instruction->id) {
...@@ -1590,6 +1602,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {...@@ -1590,6 +1602,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
1590 case IrInstructionIdMarkErrRetTracePtr:1602 case IrInstructionIdMarkErrRetTracePtr:
1591 ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);1603 ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
1592 break;1604 break;
1605 case IrInstructionIdSqrt:
1606 ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
1607 break;
1593 }1608 }
1594 fprintf(irp->f, "\n");1609 fprintf(irp->f, "\n");
1595}1610}
std/math/sqrt.zig+33-262
...@@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;...@@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;
14pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {14pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
15 const T = @typeOf(x);15 const T = @typeOf(x);
16 switch (@typeId(T)) {16 switch (@typeId(T)) {
17 TypeId.FloatLiteral => {17 TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128
18 return T(sqrt64(x));18 TypeId.Float => return @sqrt(T, x),
19 },
20 TypeId.Float => {
21 switch (T) {
22 f32 => {
23 switch (builtin.arch) {
24 builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt32(x),
25 else => return sqrt32(x),
26 }
27 },
28 f64 => {
29 switch (builtin.arch) {
30 builtin.Arch.x86_64 => return @import("x86_64/sqrt.zig").sqrt64(x),
31 else => return sqrt64(x),
32 }
33 },
34 else => @compileError("sqrt not implemented for " ++ @typeName(T)),
35 }
36 },
37 TypeId.IntLiteral => comptime {19 TypeId.IntLiteral => comptime {
38 if (x > @maxValue(u128)) {20 if (x > @maxValue(u128)) {
39 @compileError("sqrt not implemented for comptime_int greater than 128 bits");21 @compileError("sqrt not implemented for comptime_int greater than 128 bits");
...@@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ...@@ -43,269 +25,58 @@ pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typ
43 }25 }
44 return T(sqrt_int(u128, x));26 return T(sqrt_int(u128, x));
45 },27 },
46 TypeId.Int => {28 TypeId.Int => return sqrt_int(T, x),
47 return sqrt_int(T, x);
48 },
49 else => @compileError("sqrt not implemented for " ++ @typeName(T)),29 else => @compileError("sqrt not implemented for " ++ @typeName(T)),
50 }30 }
51}31}
5232
53fn sqrt32(x: f32) f32 {
54 const tiny: f32 = 1.0e-30;
55 const sign: i32 = @bitCast(i32, u32(0x80000000));
56 var ix: i32 = @bitCast(i32, x);
57
58 if ((ix & 0x7F800000) == 0x7F800000) {
59 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
60 }
61
62 // zero
63 if (ix <= 0) {
64 if (ix & ~sign == 0) {
65 return x; // sqrt (+-0) = +-0
66 }
67 if (ix < 0) {
68 return math.snan(f32);
69 }
70 }
71
72 // normalize
73 var m = ix >> 23;
74 if (m == 0) {
75 // subnormal
76 var i: i32 = 0;
77 while (ix & 0x00800000 == 0) : (i += 1) {
78 ix <<= 1;
79 }
80 m -= i - 1;
81 }
82
83 m -= 127; // unbias exponent
84 ix = (ix & 0x007FFFFF) | 0x00800000;
85
86 if (m & 1 != 0) { // odd m, double x to even
87 ix += ix;
88 }
89
90 m >>= 1; // m = [m / 2]
91
92 // sqrt(x) bit by bit
93 ix += ix;
94 var q: i32 = 0; // q = sqrt(x)
95 var s: i32 = 0;
96 var r: i32 = 0x01000000; // r = moving bit right -> left
97
98 while (r != 0) {
99 const t = s + r;
100 if (t <= ix) {
101 s = t + r;
102 ix -= t;
103 q += r;
104 }
105 ix += ix;
106 r >>= 1;
107 }
108
109 // floating add to find rounding direction
110 if (ix != 0) {
111 var z = 1.0 - tiny; // inexact
112 if (z >= 1.0) {
113 z = 1.0 + tiny;
114 if (z > 1.0) {
115 q += 2;
116 } else {
117 if (q & 1 != 0) {
118 q += 1;
119 }
120 }
121 }
122 }
123
124 ix = (q >> 1) + 0x3f000000;
125 ix += m << 23;
126 return @bitCast(f32, ix);
127}
128
129// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
130// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
131// potentially some edge cases remaining that are not handled in the same way.
132fn sqrt64(x: f64) f64 {
133 const tiny: f64 = 1.0e-300;
134 const sign: u32 = 0x80000000;
135 const u = @bitCast(u64, x);
136
137 var ix0 = u32(u >> 32);
138 var ix1 = u32(u & 0xFFFFFFFF);
139
140 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
141 if (ix0 & 0x7FF00000 == 0x7FF00000) {
142 return x * x + x;
143 }
144
145 // sqrt(+-0) = +-0
146 if (x == 0.0) {
147 return x;
148 }
149 // sqrt(-ve) = snan
150 if (ix0 & sign != 0) {
151 return math.snan(f64);
152 }
153
154 // normalize x
155 var m = i32(ix0 >> 20);
156 if (m == 0) {
157 // subnormal
158 while (ix0 == 0) {
159 m -= 21;
160 ix0 |= ix1 >> 11;
161 ix1 <<= 21;
162 }
163
164 // subnormal
165 var i: u32 = 0;
166 while (ix0 & 0x00100000 == 0) : (i += 1) {
167 ix0 <<= 1;
168 }
169 m -= i32(i) - 1;
170 ix0 |= ix1 >> u5(32 - i);
171 ix1 <<= u5(i);
172 }
173
174 // unbias exponent
175 m -= 1023;
176 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
177 if (m & 1 != 0) {
178 ix0 += ix0 + (ix1 >> 31);
179 ix1 = ix1 +% ix1;
180 }
181 m >>= 1;
182
183 // sqrt(x) bit by bit
184 ix0 += ix0 + (ix1 >> 31);
185 ix1 = ix1 +% ix1;
186
187 var q: u32 = 0;
188 var q1: u32 = 0;
189 var s0: u32 = 0;
190 var s1: u32 = 0;
191 var r: u32 = 0x00200000;
192 var t: u32 = undefined;
193 var t1: u32 = undefined;
194
195 while (r != 0) {
196 t = s0 +% r;
197 if (t <= ix0) {
198 s0 = t + r;
199 ix0 -= t;
200 q += r;
201 }
202 ix0 = ix0 +% ix0 +% (ix1 >> 31);
203 ix1 = ix1 +% ix1;
204 r >>= 1;
205 }
206
207 r = sign;
208 while (r != 0) {
209 t = s1 +% r;
210 t = s0;
211 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
212 s1 = t1 +% r;
213 if (t1 & sign == sign and s1 & sign == 0) {
214 s0 += 1;
215 }
216 ix0 -= t;
217 if (ix1 < t1) {
218 ix0 -= 1;
219 }
220 ix1 = ix1 -% t1;
221 q1 += r;
222 }
223 ix0 = ix0 +% ix0 +% (ix1 >> 31);
224 ix1 = ix1 +% ix1;
225 r >>= 1;
226 }
227
228 // rounding direction
229 if (ix0 | ix1 != 0) {
230 var z = 1.0 - tiny; // raise inexact
231 if (z >= 1.0) {
232 z = 1.0 + tiny;
233 if (q1 == 0xFFFFFFFF) {
234 q1 = 0;
235 q += 1;
236 } else if (z > 1.0) {
237 if (q1 == 0xFFFFFFFE) {
238 q += 1;
239 }
240 q1 += 2;
241 } else {
242 q1 += q1 & 1;
243 }
244 }
245 }
246
247 ix0 = (q >> 1) + 0x3FE00000;
248 ix1 = q1 >> 1;
249 if (q & 1 != 0) {
250 ix1 |= 0x80000000;
251 }
252
253 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
254 // behaviour at least.
255 var iix0 = i32(ix0);
256 iix0 = iix0 +% (m << 20);
257
258 const uz = (u64(iix0) << 32) | ix1;
259 return @bitCast(f64, uz);
260}
261
262test "math.sqrt" {33test "math.sqrt" {
263 assert(sqrt(f32(0.0)) == sqrt32(0.0));34 assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0));
264 assert(sqrt(f64(0.0)) == sqrt64(0.0));35 assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0));
265}36}
26637
267test "math.sqrt32" {38test "math.sqrt32" {
268 const epsilon = 0.000001;39 const epsilon = 0.000001;
26940
270 assert(sqrt32(0.0) == 0.0);41 assert(@sqrt(f32, 0.0) == 0.0);
271 assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon));42 assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon));
272 assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon));43 assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon));
273 assert(sqrt32(4.0) == 2.0);44 assert(@sqrt(f32, 4.0) == 2.0);
274 assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon));45 assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon));
275 assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon));46 assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon));
276 assert(sqrt32(64.0) == 8.0);47 assert(@sqrt(f32, 64.0) == 8.0);
277 assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon));48 assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon));
278 assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon));49 assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon));
279}50}
28051
281test "math.sqrt64" {52test "math.sqrt64" {
282 const epsilon = 0.000001;53 const epsilon = 0.000001;
28354
284 assert(sqrt64(0.0) == 0.0);55 assert(@sqrt(f64, 0.0) == 0.0);
285 assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon));56 assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon));
286 assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon));57 assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon));
287 assert(sqrt64(4.0) == 2.0);58 assert(@sqrt(f64, 4.0) == 2.0);
288 assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon));59 assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon));
289 assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon));60 assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon));
290 assert(sqrt64(64.0) == 8.0);61 assert(@sqrt(f64, 64.0) == 8.0);
291 assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon));62 assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon));
292 assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon));63 assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon));
293}64}
29465
295test "math.sqrt32.special" {66test "math.sqrt32.special" {
296 assert(math.isPositiveInf(sqrt32(math.inf(f32))));67 assert(math.isPositiveInf(@sqrt(f32, math.inf(f32))));
297 assert(sqrt32(0.0) == 0.0);68 assert(@sqrt(f32, 0.0) == 0.0);
298 assert(sqrt32(-0.0) == -0.0);69 assert(@sqrt(f32, -0.0) == -0.0);
299 assert(math.isNan(sqrt32(-1.0)));70 assert(math.isNan(@sqrt(f32, -1.0)));
300 assert(math.isNan(sqrt32(math.nan(f32))));71 assert(math.isNan(@sqrt(f32, math.nan(f32))));
301}72}
30273
303test "math.sqrt64.special" {74test "math.sqrt64.special" {
304 assert(math.isPositiveInf(sqrt64(math.inf(f64))));75 assert(math.isPositiveInf(@sqrt(f64, math.inf(f64))));
305 assert(sqrt64(0.0) == 0.0);76 assert(@sqrt(f64, 0.0) == 0.0);
306 assert(sqrt64(-0.0) == -0.0);77 assert(@sqrt(f64, -0.0) == -0.0);
307 assert(math.isNan(sqrt64(-1.0)));78 assert(math.isNan(@sqrt(f64, -1.0)));
308 assert(math.isNan(sqrt64(math.nan(f64))));79 assert(math.isNan(@sqrt(f64, math.nan(f64))));
309}80}
31081
311fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) {82fn sqrt_int(comptime T: type, value: T) @IntType(false, T.bit_count / 2) {
std/math/x86_64/sqrt.zig deleted-15
...@@ -1,15 +0,0 @@
1pub fn sqrt32(x: f32) f32 {
2 return asm (
3 \\sqrtss %%xmm0, %%xmm0
4 : [ret] "={xmm0}" (-> f32)
5 : [x] "{xmm0}" (x)
6 );
7}
8
9pub fn sqrt64(x: f64) f64 {
10 return asm (
11 \\sqrtsd %%xmm0, %%xmm0
12 : [ret] "={xmm0}" (-> f64)
13 : [x] "{xmm0}" (x)
14 );
15}
std/special/builtin.zig+209
...@@ -194,3 +194,212 @@ fn isNan(comptime T: type, bits: T) bool {...@@ -194,3 +194,212 @@ fn isNan(comptime T: type, bits: T) bool {
194 unreachable;194 unreachable;
195 }195 }
196}196}
197
198// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
199// behaviour. Most intermediate i32 values are changed to u32 where appropriate but there are
200// potentially some edge cases remaining that are not handled in the same way.
201export fn sqrt(x: f64) f64 {
202 const tiny: f64 = 1.0e-300;
203 const sign: u32 = 0x80000000;
204 const u = @bitCast(u64, x);
205
206 var ix0 = u32(u >> 32);
207 var ix1 = u32(u & 0xFFFFFFFF);
208
209 // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = nan
210 if (ix0 & 0x7FF00000 == 0x7FF00000) {
211 return x * x + x;
212 }
213
214 // sqrt(+-0) = +-0
215 if (x == 0.0) {
216 return x;
217 }
218 // sqrt(-ve) = snan
219 if (ix0 & sign != 0) {
220 return math.snan(f64);
221 }
222
223 // normalize x
224 var m = i32(ix0 >> 20);
225 if (m == 0) {
226 // subnormal
227 while (ix0 == 0) {
228 m -= 21;
229 ix0 |= ix1 >> 11;
230 ix1 <<= 21;
231 }
232
233 // subnormal
234 var i: u32 = 0;
235 while (ix0 & 0x00100000 == 0) : (i += 1) {
236 ix0 <<= 1;
237 }
238 m -= i32(i) - 1;
239 ix0 |= ix1 >> u5(32 - i);
240 ix1 <<= u5(i);
241 }
242
243 // unbias exponent
244 m -= 1023;
245 ix0 = (ix0 & 0x000FFFFF) | 0x00100000;
246 if (m & 1 != 0) {
247 ix0 += ix0 + (ix1 >> 31);
248 ix1 = ix1 +% ix1;
249 }
250 m >>= 1;
251
252 // sqrt(x) bit by bit
253 ix0 += ix0 + (ix1 >> 31);
254 ix1 = ix1 +% ix1;
255
256 var q: u32 = 0;
257 var q1: u32 = 0;
258 var s0: u32 = 0;
259 var s1: u32 = 0;
260 var r: u32 = 0x00200000;
261 var t: u32 = undefined;
262 var t1: u32 = undefined;
263
264 while (r != 0) {
265 t = s0 +% r;
266 if (t <= ix0) {
267 s0 = t + r;
268 ix0 -= t;
269 q += r;
270 }
271 ix0 = ix0 +% ix0 +% (ix1 >> 31);
272 ix1 = ix1 +% ix1;
273 r >>= 1;
274 }
275
276 r = sign;
277 while (r != 0) {
278 t = s1 +% r;
279 t = s0;
280 if (t < ix0 or (t == ix0 and t1 <= ix1)) {
281 s1 = t1 +% r;
282 if (t1 & sign == sign and s1 & sign == 0) {
283 s0 += 1;
284 }
285 ix0 -= t;
286 if (ix1 < t1) {
287 ix0 -= 1;
288 }
289 ix1 = ix1 -% t1;
290 q1 += r;
291 }
292 ix0 = ix0 +% ix0 +% (ix1 >> 31);
293 ix1 = ix1 +% ix1;
294 r >>= 1;
295 }
296
297 // rounding direction
298 if (ix0 | ix1 != 0) {
299 var z = 1.0 - tiny; // raise inexact
300 if (z >= 1.0) {
301 z = 1.0 + tiny;
302 if (q1 == 0xFFFFFFFF) {
303 q1 = 0;
304 q += 1;
305 } else if (z > 1.0) {
306 if (q1 == 0xFFFFFFFE) {
307 q += 1;
308 }
309 q1 += 2;
310 } else {
311 q1 += q1 & 1;
312 }
313 }
314 }
315
316 ix0 = (q >> 1) + 0x3FE00000;
317 ix1 = q1 >> 1;
318 if (q & 1 != 0) {
319 ix1 |= 0x80000000;
320 }
321
322 // NOTE: musl here appears to rely on signed twos-complement wraparound. +% has the same
323 // behaviour at least.
324 var iix0 = i32(ix0);
325 iix0 = iix0 +% (m << 20);
326
327 const uz = (u64(iix0) << 32) | ix1;
328 return @bitCast(f64, uz);
329}
330
331export fn sqrtf(x: f32) f32 {
332 const tiny: f32 = 1.0e-30;
333 const sign: i32 = @bitCast(i32, u32(0x80000000));
334 var ix: i32 = @bitCast(i32, x);
335
336 if ((ix & 0x7F800000) == 0x7F800000) {
337 return x * x + x; // sqrt(nan) = nan, sqrt(+inf) = +inf, sqrt(-inf) = snan
338 }
339
340 // zero
341 if (ix <= 0) {
342 if (ix & ~sign == 0) {
343 return x; // sqrt (+-0) = +-0
344 }
345 if (ix < 0) {
346 return math.snan(f32);
347 }
348 }
349
350 // normalize
351 var m = ix >> 23;
352 if (m == 0) {
353 // subnormal
354 var i: i32 = 0;
355 while (ix & 0x00800000 == 0) : (i += 1) {
356 ix <<= 1;
357 }
358 m -= i - 1;
359 }
360
361 m -= 127; // unbias exponent
362 ix = (ix & 0x007FFFFF) | 0x00800000;
363
364 if (m & 1 != 0) { // odd m, double x to even
365 ix += ix;
366 }
367
368 m >>= 1; // m = [m / 2]
369
370 // sqrt(x) bit by bit
371 ix += ix;
372 var q: i32 = 0; // q = sqrt(x)
373 var s: i32 = 0;
374 var r: i32 = 0x01000000; // r = moving bit right -> left
375
376 while (r != 0) {
377 const t = s + r;
378 if (t <= ix) {
379 s = t + r;
380 ix -= t;
381 q += r;
382 }
383 ix += ix;
384 r >>= 1;
385 }
386
387 // floating add to find rounding direction
388 if (ix != 0) {
389 var z = 1.0 - tiny; // inexact
390 if (z >= 1.0) {
391 z = 1.0 + tiny;
392 if (z > 1.0) {
393 q += 2;
394 } else {
395 if (q & 1 != 0) {
396 q += 1;
397 }
398 }
399 }
400 }
401
402 ix = (q >> 1) + 0x3f000000;
403 ix += m << 23;
404 return @bitCast(f32, ix);
405}
test/cases/math.zig+16
...@@ -402,3 +402,19 @@ test "comptime float rem int" {...@@ -402,3 +402,19 @@ test "comptime float rem int" {
402 assert(x == 1.0);402 assert(x == 1.0);
403 }403 }
404}404}
405
406test "@sqrt" {
407 testSqrt(f64, 12.0);
408 comptime testSqrt(f64, 12.0);
409 testSqrt(f32, 13.0);
410 comptime testSqrt(f32, 13.0);
411
412 const x = 14.0;
413 const y = x * x;
414 const z = @sqrt(@typeOf(y), y);
415 comptime assert(z == x);
416}
417
418fn testSqrt(comptime T: type, x: T) void {
419 assert(@sqrt(T, x * x) == x);
420}