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
498498 "math/tan.zig"
499499 "math/tanh.zig"
500500 "math/trunc.zig"
501 "math/x86_64/sqrt.zig"
502501 "mem.zig"
503502 "net.zig"
504503 "os/child_process.zig"
doc/langref.html.in+11-1
......@@ -4669,6 +4669,16 @@ pub const FloatMode = enum {
46694669 The result is a target-specific compile time constant.
46704670 </p>
46714671 {#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#}
46724682 {#header_open|@subWithOverflow#}
46734683 <pre><code class="zig">@subWithOverflow(comptime T: type, a: T, b: T, result: &T) -&gt; bool</code></pre>
46744684 <p>
......@@ -5991,7 +6001,7 @@ hljs.registerLanguage("zig", function(t) {
59916001 a = t.IR + "\\s*\\(",
59926002 c = {
59936003 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",
59956005 literal: "true false null undefined"
59966006 },
59976007 n = [e, t.CLCM, t.CBCM, s, r];
src/all_types.hpp+11-1
......@@ -1317,6 +1317,7 @@ enum BuiltinFnId {
13171317 BuiltinFnIdDivFloor,
13181318 BuiltinFnIdRem,
13191319 BuiltinFnIdMod,
1320 BuiltinFnIdSqrt,
13201321 BuiltinFnIdTruncate,
13211322 BuiltinFnIdIntType,
13221323 BuiltinFnIdSetCold,
......@@ -1413,6 +1414,7 @@ enum ZigLLVMFnId {
14131414 ZigLLVMFnIdOverflowArithmetic,
14141415 ZigLLVMFnIdFloor,
14151416 ZigLLVMFnIdCeil,
1417 ZigLLVMFnIdSqrt,
14161418};
14171419
14181420enum AddSubMul {
......@@ -1433,7 +1435,7 @@ struct ZigLLVMFnKey {
14331435 } clz;
14341436 struct {
14351437 uint32_t bit_count;
1436 } floor_ceil;
1438 } floating;
14371439 struct {
14381440 AddSubMul add_sub_mul;
14391441 uint32_t bit_count;
......@@ -2047,6 +2049,7 @@ enum IrInstructionId {
20472049 IrInstructionIdAddImplicitReturnType,
20482050 IrInstructionIdMergeErrRetTraces,
20492051 IrInstructionIdMarkErrRetTracePtr,
2052 IrInstructionIdSqrt,
20502053};
20512054
20522055struct IrInstruction {
......@@ -3036,6 +3039,13 @@ struct IrInstructionMarkErrRetTracePtr {
30363039 IrInstruction *err_ret_trace_ptr;
30373040};
30383041
3042struct IrInstructionSqrt {
3043 IrInstruction base;
3044
3045 IrInstruction *type;
3046 IrInstruction *op;
3047};
3048
30393049static const size_t slice_ptr_index = 0;
30403050static 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) {
58015801 case ZigLLVMFnIdClz:
58025802 return (uint32_t)(x.data.clz.bit_count) * (uint32_t)2428952817;
58035803 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;
58055805 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;
58075809 case ZigLLVMFnIdOverflowArithmetic:
58085810 return ((uint32_t)(x.data.overflow_arithmetic.bit_count) * 87135777) +
58095811 ((uint32_t)(x.data.overflow_arithmetic.add_sub_mul) * 31640542) +
......@@ -5822,7 +5824,8 @@ bool zig_llvm_fn_key_eql(ZigLLVMFnKey a, ZigLLVMFnKey b) {
58225824 return a.data.clz.bit_count == b.data.clz.bit_count;
58235825 case ZigLLVMFnIdFloor:
58245826 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;
58265829 case ZigLLVMFnIdOverflowArithmetic:
58275830 return (a.data.overflow_arithmetic.bit_count == b.data.overflow_arithmetic.bit_count) &&
58285831 (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) {
181181 f128M_roundToInt(&bigfloat->value, softfloat_round_minMag, false, &floored);
182182 return !f128M_eq(&floored, &bigfloat->value);
183183}
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
4242void bigfloat_div_floor(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
4343void bigfloat_rem(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
4444void bigfloat_mod(BigFloat *dest, const BigFloat *op1, const BigFloat *op2);
45void bigfloat_sqrt(BigFloat *dest, const BigFloat *op);
4546void bigfloat_append_buf(Buf *buf, const BigFloat *op);
4647Cmp 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,
717717 return fn_val;
718718}
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) {
721721 assert(type_entry->id == TypeTableEntryIdFloat);
722722
723723 ZigLLVMFnKey key = {};
724724 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
727727 auto existing_entry = g->llvm_fn_table.maybe_get(key);
728728 if (existing_entry)
......@@ -733,6 +733,8 @@ static LLVMValueRef get_floor_ceil_fn(CodeGen *g, TypeTableEntry *type_entry, Zi
733733 name = "floor";
734734 } else if (fn_id == ZigLLVMFnIdCeil) {
735735 name = "ceil";
736 } else if (fn_id == ZigLLVMFnIdSqrt) {
737 name = "sqrt";
736738 } else {
737739 zig_unreachable();
738740 }
......@@ -1900,7 +1902,7 @@ static LLVMValueRef gen_floor(CodeGen *g, LLVMValueRef val, TypeTableEntry *type
19001902 if (type_entry->id == TypeTableEntryIdInt)
19011903 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);
19041906 return LLVMBuildCall(g->builder, floor_fn, &val, 1, "");
19051907}
19061908
......@@ -1908,7 +1910,7 @@ static LLVMValueRef gen_ceil(CodeGen *g, LLVMValueRef val, TypeTableEntry *type_
19081910 if (type_entry->id == TypeTableEntryIdInt)
19091911 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);
19121914 return LLVMBuildCall(g->builder, ceil_fn, &val, 1, "");
19131915}
19141916
......@@ -3247,10 +3249,12 @@ static LLVMValueRef get_int_builtin_fn(CodeGen *g, TypeTableEntry *int_type, Bui
32473249 fn_name = "cttz";
32483250 key.id = ZigLLVMFnIdCtz;
32493251 key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3250 } else {
3252 } else if (fn_id == BuiltinFnIdClz) {
32513253 fn_name = "ctlz";
32523254 key.id = ZigLLVMFnIdClz;
32533255 key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count;
3256 } else {
3257 zig_unreachable();
32543258 }
32553259
32563260 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
44024406 return nullptr;
44034407}
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
44054416static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
44064417 AstNode *source_node = instruction->source_node;
44074418 Scope *scope = instruction->scope;
......@@ -4623,6 +4634,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
46234634 return ir_render_merge_err_ret_traces(g, executable, (IrInstructionMergeErrRetTraces *)instruction);
46244635 case IrInstructionIdMarkErrRetTracePtr:
46254636 return ir_render_mark_err_ret_trace_ptr(g, executable, (IrInstructionMarkErrRetTracePtr *)instruction);
4637 case IrInstructionIdSqrt:
4638 return ir_render_sqrt(g, executable, (IrInstructionSqrt *)instruction);
46264639 }
46274640 zig_unreachable();
46284641}
......@@ -6109,6 +6122,7 @@ static void define_builtin_fns(CodeGen *g) {
61096122 create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2);
61106123 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
61116124 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
6125 create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 2);
61126126 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
61136127 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
61146128 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
src/ir.cpp+94
......@@ -733,6 +733,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionMarkErrRetTraceP
733733 return IrInstructionIdMarkErrRetTracePtr;
734734}
735735
736static constexpr IrInstructionId ir_instruction_id(IrInstructionSqrt *) {
737 return IrInstructionIdSqrt;
738}
739
736740template<typename T>
737741static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
738742 T *special_instruction = allocate<T>(1);
......@@ -2731,6 +2735,17 @@ static IrInstruction *ir_build_mark_err_ret_trace_ptr(IrBuilder *irb, Scope *sco
27312735 return &instruction->base;
27322736}
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
27342749static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
27352750 results[ReturnKindUnconditional] = 0;
27362751 results[ReturnKindError] = 0;
......@@ -3845,6 +3860,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
38453860
38463861 return ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true);
38473862 }
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 }
38483877 case BuiltinFnIdTruncate:
38493878 {
38503879 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 *
1803118060 return result->value.type;
1803218061}
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
1803418125static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
1803518126 switch (instruction->id) {
1803618127 case IrInstructionIdInvalid:
......@@ -18278,6 +18369,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
1827818369 return ir_analyze_instruction_merge_err_ret_traces(ira, (IrInstructionMergeErrRetTraces *)instruction);
1827918370 case IrInstructionIdMarkErrRetTracePtr:
1828018371 return ir_analyze_instruction_mark_err_ret_trace_ptr(ira, (IrInstructionMarkErrRetTracePtr *)instruction);
18372 case IrInstructionIdSqrt:
18373 return ir_analyze_instruction_sqrt(ira, (IrInstructionSqrt *)instruction);
1828118374 }
1828218375 zig_unreachable();
1828318376}
......@@ -18490,6 +18583,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
1849018583 case IrInstructionIdCoroFree:
1849118584 case IrInstructionIdCoroPromise:
1849218585 case IrInstructionIdPromiseResultType:
18586 case IrInstructionIdSqrt:
1849318587 return false;
1849418588
1849518589 case IrInstructionIdAsm:
src/ir_print.cpp+15
......@@ -1204,6 +1204,18 @@ static void ir_print_mark_err_ret_trace_ptr(IrPrint *irp, IrInstructionMarkErrRe
12041204 fprintf(irp->f, ")");
12051205}
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
12071219static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
12081220 ir_print_prefix(irp, instruction);
12091221 switch (instruction->id) {
......@@ -1590,6 +1602,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
15901602 case IrInstructionIdMarkErrRetTracePtr:
15911603 ir_print_mark_err_ret_trace_ptr(irp, (IrInstructionMarkErrRetTracePtr *)instruction);
15921604 break;
1605 case IrInstructionIdSqrt:
1606 ir_print_sqrt(irp, (IrInstructionSqrt *)instruction);
1607 break;
15931608 }
15941609 fprintf(irp->f, "\n");
15951610}
std/math/sqrt.zig+33-262
......@@ -14,26 +14,8 @@ const TypeId = builtin.TypeId;
1414pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
1515 const T = @typeOf(x);
1616 switch (@typeId(T)) {
17 TypeId.FloatLiteral => {
18 return T(sqrt64(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 },
17 TypeId.FloatLiteral => return T(@sqrt(f64, x)), // TODO upgrade to f128
18 TypeId.Float => return @sqrt(T, x),
3719 TypeId.IntLiteral => comptime {
3820 if (x > @maxValue(u128)) {
3921 @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
4325 }
4426 return T(sqrt_int(u128, x));
4527 },
46 TypeId.Int => {
47 return sqrt_int(T, x);
48 },
28 TypeId.Int => return sqrt_int(T, x),
4929 else => @compileError("sqrt not implemented for " ++ @typeName(T)),
5030 }
5131}
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
26233test "math.sqrt" {
263 assert(sqrt(f32(0.0)) == sqrt32(0.0));
264 assert(sqrt(f64(0.0)) == sqrt64(0.0));
34 assert(sqrt(f32(0.0)) == @sqrt(f32, 0.0));
35 assert(sqrt(f64(0.0)) == @sqrt(f64, 0.0));
26536}
26637
26738test "math.sqrt32" {
26839 const epsilon = 0.000001;
26940
270 assert(sqrt32(0.0) == 0.0);
271 assert(math.approxEq(f32, sqrt32(2.0), 1.414214, epsilon));
272 assert(math.approxEq(f32, sqrt32(3.6), 1.897367, epsilon));
273 assert(sqrt32(4.0) == 2.0);
274 assert(math.approxEq(f32, sqrt32(7.539840), 2.745877, epsilon));
275 assert(math.approxEq(f32, sqrt32(19.230934), 4.385309, epsilon));
276 assert(sqrt32(64.0) == 8.0);
277 assert(math.approxEq(f32, sqrt32(64.1), 8.006248, epsilon));
278 assert(math.approxEq(f32, sqrt32(8942.230469), 94.563370, epsilon));
41 assert(@sqrt(f32, 0.0) == 0.0);
42 assert(math.approxEq(f32, @sqrt(f32, 2.0), 1.414214, epsilon));
43 assert(math.approxEq(f32, @sqrt(f32, 3.6), 1.897367, epsilon));
44 assert(@sqrt(f32, 4.0) == 2.0);
45 assert(math.approxEq(f32, @sqrt(f32, 7.539840), 2.745877, epsilon));
46 assert(math.approxEq(f32, @sqrt(f32, 19.230934), 4.385309, epsilon));
47 assert(@sqrt(f32, 64.0) == 8.0);
48 assert(math.approxEq(f32, @sqrt(f32, 64.1), 8.006248, epsilon));
49 assert(math.approxEq(f32, @sqrt(f32, 8942.230469), 94.563370, epsilon));
27950}
28051
28152test "math.sqrt64" {
28253 const epsilon = 0.000001;
28354
284 assert(sqrt64(0.0) == 0.0);
285 assert(math.approxEq(f64, sqrt64(2.0), 1.414214, epsilon));
286 assert(math.approxEq(f64, sqrt64(3.6), 1.897367, epsilon));
287 assert(sqrt64(4.0) == 2.0);
288 assert(math.approxEq(f64, sqrt64(7.539840), 2.745877, epsilon));
289 assert(math.approxEq(f64, sqrt64(19.230934), 4.385309, epsilon));
290 assert(sqrt64(64.0) == 8.0);
291 assert(math.approxEq(f64, sqrt64(64.1), 8.006248, epsilon));
292 assert(math.approxEq(f64, sqrt64(8942.230469), 94.563367, epsilon));
55 assert(@sqrt(f64, 0.0) == 0.0);
56 assert(math.approxEq(f64, @sqrt(f64, 2.0), 1.414214, epsilon));
57 assert(math.approxEq(f64, @sqrt(f64, 3.6), 1.897367, epsilon));
58 assert(@sqrt(f64, 4.0) == 2.0);
59 assert(math.approxEq(f64, @sqrt(f64, 7.539840), 2.745877, epsilon));
60 assert(math.approxEq(f64, @sqrt(f64, 19.230934), 4.385309, epsilon));
61 assert(@sqrt(f64, 64.0) == 8.0);
62 assert(math.approxEq(f64, @sqrt(f64, 64.1), 8.006248, epsilon));
63 assert(math.approxEq(f64, @sqrt(f64, 8942.230469), 94.563367, epsilon));
29364}
29465
29566test "math.sqrt32.special" {
296 assert(math.isPositiveInf(sqrt32(math.inf(f32))));
297 assert(sqrt32(0.0) == 0.0);
298 assert(sqrt32(-0.0) == -0.0);
299 assert(math.isNan(sqrt32(-1.0)));
300 assert(math.isNan(sqrt32(math.nan(f32))));
67 assert(math.isPositiveInf(@sqrt(f32, math.inf(f32))));
68 assert(@sqrt(f32, 0.0) == 0.0);
69 assert(@sqrt(f32, -0.0) == -0.0);
70 assert(math.isNan(@sqrt(f32, -1.0)));
71 assert(math.isNan(@sqrt(f32, math.nan(f32))));
30172}
30273
30374test "math.sqrt64.special" {
304 assert(math.isPositiveInf(sqrt64(math.inf(f64))));
305 assert(sqrt64(0.0) == 0.0);
306 assert(sqrt64(-0.0) == -0.0);
307 assert(math.isNan(sqrt64(-1.0)));
308 assert(math.isNan(sqrt64(math.nan(f64))));
75 assert(math.isPositiveInf(@sqrt(f64, math.inf(f64))));
76 assert(@sqrt(f64, 0.0) == 0.0);
77 assert(@sqrt(f64, -0.0) == -0.0);
78 assert(math.isNan(@sqrt(f64, -1.0)));
79 assert(math.isNan(@sqrt(f64, math.nan(f64))));
30980}
31081
31182fn 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 {
194194 unreachable;
195195 }
196196}
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" {
402402 assert(x == 1.0);
403403 }
404404}
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}