| author | |
| committer | |
| log | cbe468a787e93d57ca64799e715ea7493f4a6ebb |
| tree | 399a64733142b46d1568982258234db1be107222 |
| parent | b6aebc41177dec5d7dddcc7594e5435d54f17963 |
| parent | 4affe94d79c048fd2dc0c071146e0e842f97b63e |
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/309065 files changed, 307 insertions(+), 15 deletions(-)
lib/std/zig/AstGen.zig+41-4| ... | ... | @@ -9266,10 +9266,10 @@ fn builtinCall( |
| 9266 | 9266 | .log => return floatUnOp(gz, scope, ri, node, params[0], .log), |
| 9267 | 9267 | .log2 => return floatUnOp(gz, scope, ri, node, params[0], .log2), |
| 9268 | 9268 | .log10 => return floatUnOp(gz, scope, ri, node, params[0], .log10), |
| 9269 | .floor => return floatUnOp(gz, scope, ri, node, params[0], .floor), | |
| 9270 | .ceil => return floatUnOp(gz, scope, ri, node, params[0], .ceil), | |
| 9271 | .trunc => return floatUnOp(gz, scope, ri, node, params[0], .trunc), | |
| 9272 | .round => return floatUnOp(gz, scope, ri, node, params[0], .round), | |
| 9269 | .floor => return floatRoundOp(gz, scope, ri, node, params[0], .floor), | |
| 9270 | .ceil => return floatRoundOp(gz, scope, ri, node, params[0], .ceil), | |
| 9271 | .trunc => return floatRoundOp(gz, scope, ri, node, params[0], .trunc), | |
| 9272 | .round => return floatRoundOp(gz, scope, ri, node, params[0], .round), | |
| 9273 | 9273 | |
| 9274 | 9274 | .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name), |
| 9275 | 9275 | .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name), |
| ... | ... | @@ -9819,6 +9819,43 @@ fn simpleUnOp( |
| 9819 | 9819 | return rvalue(gz, ri, result, node); |
| 9820 | 9820 | } |
| 9821 | 9821 | |
| 9822 | fn floatRoundOp( | |
| 9823 | gz: *GenZir, | |
| 9824 | scope: *Scope, | |
| 9825 | ri: ResultInfo, | |
| 9826 | node: Ast.Node.Index, | |
| 9827 | operand_node: Ast.Node.Index, | |
| 9828 | float_tag: Zir.Inst.Tag, | |
| 9829 | ) InnerError!Zir.Inst.Ref { | |
| 9830 | if (try ri.rl.resultType(gz, node)) |dest_type| { | |
| 9831 | const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); | |
| 9832 | ||
| 9833 | const operand_ty_inst = try gz.addExtendedPayload(.round_op_ty, Zir.Inst.UnNode{ | |
| 9834 | .node = gz.nodeIndexToRelative(node), | |
| 9835 | .operand = dest_type, | |
| 9836 | }); | |
| 9837 | ||
| 9838 | const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = operand_ty_inst } }, operand_node); | |
| 9839 | ||
| 9840 | try emitDbgStmt(gz, cursor); | |
| 9841 | const round_op: Zir.Inst.RoundOp = switch (float_tag) { | |
| 9842 | .round => .round, | |
| 9843 | .floor => .floor, | |
| 9844 | .ceil => .ceil, | |
| 9845 | .trunc => .trunc, | |
| 9846 | else => unreachable, | |
| 9847 | }; | |
| 9848 | const result = try gz.addExtendedPayloadSmall(.round_op, @intFromEnum(round_op), Zir.Inst.BinNode{ | |
| 9849 | .node = gz.nodeIndexToRelative(node), | |
| 9850 | .lhs = dest_type, | |
| 9851 | .rhs = operand, | |
| 9852 | }); | |
| 9853 | return rvalue(gz, ri, result, node); | |
| 9854 | } else { | |
| 9855 | return floatUnOp(gz, scope, ri, node, operand_node, float_tag); | |
| 9856 | } | |
| 9857 | } | |
| 9858 | ||
| 9822 | 9859 | fn floatUnOp( |
| 9823 | 9860 | gz: *GenZir, |
| 9824 | 9861 | scope: *Scope, |
lib/std/zig/Zir.zig+17| ... | ... | @@ -2009,6 +2009,14 @@ pub const Inst = struct { |
| 2009 | 2009 | /// `operand` is payload index to `BinNode`. |
| 2010 | 2010 | /// `small` is unused. |
| 2011 | 2011 | shl_with_overflow, |
| 2012 | /// `@round`, `@floor`, `@ceil`, or `@trunc`, with a result type. | |
| 2013 | /// `operand` is payload index to `BinNode`. | |
| 2014 | /// `small` is a `RoundOp` representing the specific operation being performed. | |
| 2015 | round_op, | |
| 2016 | /// Returns the type for the operand of a rounding op. | |
| 2017 | /// `operand` is `UnNode`. | |
| 2018 | /// `small` is unused. | |
| 2019 | round_op_ty, | |
| 2012 | 2020 | /// `operand` is payload index to `UnNode`. |
| 2013 | 2021 | c_undef, |
| 2014 | 2022 | /// `operand` is payload index to `UnNode`. |
| ... | ... | @@ -3233,6 +3241,13 @@ pub const Inst = struct { |
| 3233 | 3241 | string_to_union_field_attrs, |
| 3234 | 3242 | }; |
| 3235 | 3243 | |
| 3244 | pub const RoundOp = enum(u16) { | |
| 3245 | round, | |
| 3246 | floor, | |
| 3247 | ceil, | |
| 3248 | trunc, | |
| 3249 | }; | |
| 3250 | ||
| 3236 | 3251 | pub const UnNode = struct { |
| 3237 | 3252 | node: Ast.Node.Offset, |
| 3238 | 3253 | operand: Ref, |
| ... | ... | @@ -4344,6 +4359,7 @@ fn findTrackableInner( |
| 4344 | 4359 | .sub_with_overflow, |
| 4345 | 4360 | .mul_with_overflow, |
| 4346 | 4361 | .shl_with_overflow, |
| 4362 | .round_op, | |
| 4347 | 4363 | .c_undef, |
| 4348 | 4364 | .c_include, |
| 4349 | 4365 | .c_define, |
| ... | ... | @@ -4385,6 +4401,7 @@ fn findTrackableInner( |
| 4385 | 4401 | .dbg_empty_stmt, |
| 4386 | 4402 | .astgen_error, |
| 4387 | 4403 | .float_op_result_ty, |
| 4404 | .round_op_ty, | |
| 4388 | 4405 | => return, |
| 4389 | 4406 | |
| 4390 | 4407 | // `@TypeOf` has a body. |
src/Sema.zig+173-11| ... | ... | @@ -1405,6 +1405,8 @@ fn analyzeBodyInner( |
| 1405 | 1405 | .@"asm" => try sema.zirAsm( block, extended, false), |
| 1406 | 1406 | .asm_expr => try sema.zirAsm( block, extended, true), |
| 1407 | 1407 | .typeof_peer => try sema.zirTypeofPeer( block, extended, inst), |
| 1408 | .round_op => try sema.zirRoundCast( block, extended), | |
| 1409 | .round_op_ty => try sema.zirRoundOpType( block, extended), | |
| 1408 | 1410 | .compile_log => try sema.zirCompileLog( block, extended), |
| 1409 | 1411 | .min_multi => try sema.zirMinMaxMulti( block, extended, .min), |
| 1410 | 1412 | .max_multi => try sema.zirMinMaxMulti( block, extended, .max), |
| ... | ... | @@ -19685,21 +19687,16 @@ fn maybeConstantUnaryMath( |
| 19685 | 19687 | return null; |
| 19686 | 19688 | } |
| 19687 | 19689 | |
| 19688 | fn zirUnaryMath( | |
| 19690 | fn unaryMath( | |
| 19689 | 19691 | sema: *Sema, |
| 19690 | 19692 | block: *Block, |
| 19691 | inst: Zir.Inst.Index, | |
| 19693 | operand_src: LazySrcLoc, | |
| 19694 | operand: Air.Inst.Ref, | |
| 19692 | 19695 | air_tag: Air.Inst.Tag, |
| 19693 | 19696 | comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, |
| 19694 | 19697 | ) CompileError!Air.Inst.Ref { |
| 19695 | const tracy = trace(@src()); | |
| 19696 | defer tracy.end(); | |
| 19697 | ||
| 19698 | 19698 | const pt = sema.pt; |
| 19699 | 19699 | const zcu = pt.zcu; |
| 19700 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | |
| 19701 | const operand = sema.resolveInst(inst_data.operand); | |
| 19702 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); | |
| 19703 | 19700 | const operand_ty = sema.typeOf(operand); |
| 19704 | 19701 | const scalar_ty = operand_ty.scalarType(zcu); |
| 19705 | 19702 | |
| ... | ... | @@ -19719,6 +19716,23 @@ fn zirUnaryMath( |
| 19719 | 19716 | }; |
| 19720 | 19717 | } |
| 19721 | 19718 | |
| 19719 | fn zirUnaryMath( | |
| 19720 | sema: *Sema, | |
| 19721 | block: *Block, | |
| 19722 | inst: Zir.Inst.Index, | |
| 19723 | air_tag: Air.Inst.Tag, | |
| 19724 | comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, | |
| 19725 | ) CompileError!Air.Inst.Ref { | |
| 19726 | const tracy = trace(@src()); | |
| 19727 | defer tracy.end(); | |
| 19728 | ||
| 19729 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; | |
| 19730 | const operand = sema.resolveInst(inst_data.operand); | |
| 19731 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); | |
| 19732 | ||
| 19733 | return sema.unaryMath(block, operand_src, operand, air_tag, eval); | |
| 19734 | } | |
| 19735 | ||
| 19722 | 19736 | fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19723 | 19737 | const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; |
| 19724 | 19738 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| ... | ... | @@ -20900,6 +20914,130 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 20900 | 20914 | }, dest_ty, operand); |
| 20901 | 20915 | } |
| 20902 | 20916 | |
| 20917 | fn zirRoundCast( | |
| 20918 | sema: *Sema, | |
| 20919 | block: *Block, | |
| 20920 | extended: Zir.Inst.Extended.InstData, | |
| 20921 | ) CompileError!Air.Inst.Ref { | |
| 20922 | const pt = sema.pt; | |
| 20923 | const zcu = pt.zcu; | |
| 20924 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; | |
| 20925 | const src = block.nodeOffset(extra.node); | |
| 20926 | const operand_src = block.builtinCallArgSrc(extra.node, 0); | |
| 20927 | ||
| 20928 | const operand = sema.resolveInst(extra.rhs); | |
| 20929 | ||
| 20930 | const round_op: Zir.Inst.RoundOp = @enumFromInt(extended.small); | |
| 20931 | const mode: IntFromFloatMode = switch (round_op) { | |
| 20932 | .round => .round, | |
| 20933 | .floor => .floor, | |
| 20934 | .ceil => .ceil, | |
| 20935 | .trunc => .truncate, | |
| 20936 | }; | |
| 20937 | ||
| 20938 | const dest_ty = (try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse switch (mode) { | |
| 20939 | // zig fmt: off | |
| 20940 | .round => return sema.unaryMath(block, operand_src, operand, .round, Value.round), | |
| 20941 | .floor => return sema.unaryMath(block, operand_src, operand, .floor, Value.floor), | |
| 20942 | .ceil => return sema.unaryMath(block, operand_src, operand, .ceil, Value.ceil), | |
| 20943 | .truncate => return sema.unaryMath(block, operand_src, operand, .trunc_float, Value.trunc), | |
| 20944 | // zig fmt: on | |
| 20945 | .exact => unreachable, | |
| 20946 | }).optEuBaseType(zcu); | |
| 20947 | ||
| 20948 | const operand_ty = sema.typeOf(operand); | |
| 20949 | ||
| 20950 | try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); | |
| 20951 | ||
| 20952 | const dest_scalar_ty = dest_ty.scalarType(zcu); | |
| 20953 | const operand_scalar_ty = operand_ty.scalarType(zcu); | |
| 20954 | ||
| 20955 | switch (operand_scalar_ty.zigTypeTag(zcu)) { | |
| 20956 | .comptime_float, .float => {}, | |
| 20957 | else => return sema.fail( | |
| 20958 | block, | |
| 20959 | operand_src, | |
| 20960 | "expected float or vector type, found '{f}'", | |
| 20961 | .{operand_ty.fmt(pt)}, | |
| 20962 | ), | |
| 20963 | } | |
| 20964 | ||
| 20965 | switch (dest_scalar_ty.zigTypeTag(zcu)) { | |
| 20966 | .float, .comptime_float => { | |
| 20967 | const coerced_operand = try sema.coerce(block, dest_ty, operand, operand_src); | |
| 20968 | ||
| 20969 | const result_ref = switch (mode) { | |
| 20970 | .round => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.round), | |
| 20971 | .floor => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.floor), | |
| 20972 | .ceil => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.ceil), | |
| 20973 | .truncate => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.trunc), | |
| 20974 | .exact => unreachable, | |
| 20975 | }; | |
| 20976 | ||
| 20977 | if (result_ref) |ref| return ref; | |
| 20978 | ||
| 20979 | const air_tag: Air.Inst.Tag = switch (mode) { | |
| 20980 | .round => .round, | |
| 20981 | .floor => .floor, | |
| 20982 | .ceil => .ceil, | |
| 20983 | .truncate => .trunc_float, | |
| 20984 | .exact => unreachable, | |
| 20985 | }; | |
| 20986 | ||
| 20987 | try sema.requireRuntimeBlock(block, operand_src, null); | |
| 20988 | return block.addUnOp(air_tag, coerced_operand); | |
| 20989 | }, | |
| 20990 | .int, .comptime_int => {}, | |
| 20991 | else => return sema.fail( | |
| 20992 | block, | |
| 20993 | src, | |
| 20994 | "expected integer, float, or vector of either integers or floats, found '{f}'", | |
| 20995 | .{dest_ty.fmt(pt)}, | |
| 20996 | ), | |
| 20997 | } | |
| 20998 | ||
| 20999 | if (sema.resolveValue(operand)) |operand_val| { | |
| 21000 | const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, mode); | |
| 21001 | return .fromValue(result_val); | |
| 21002 | } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { | |
| 21003 | return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int }); | |
| 21004 | } | |
| 21005 | ||
| 21006 | try sema.requireRuntimeBlock(block, src, operand_src); | |
| 21007 | ||
| 21008 | if (dest_scalar_ty.intInfo(zcu).bits == 0) { | |
| 21009 | if (block.wantSafety()) { | |
| 21010 | const abs_ref = try block.addTyOp(.abs, operand_ty, operand); | |
| 21011 | const is_vector = dest_ty.zigTypeTag(zcu) == .vector; | |
| 21012 | const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref; | |
| 21013 | const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern()); | |
| 21014 | const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref); | |
| 21015 | try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds); | |
| 21016 | } | |
| 21017 | const scalar_val = try pt.intValue(dest_scalar_ty, 0); | |
| 21018 | return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern()); | |
| 21019 | } | |
| 21020 | ||
| 21021 | const safe = block.wantSafety(); | |
| 21022 | ||
| 21023 | if (safe) { | |
| 21024 | try sema.preparePanicId(src, .integer_part_out_of_bounds); | |
| 21025 | } | |
| 21026 | ||
| 21027 | const uncasted_result: Air.Inst.Ref = switch (mode) { | |
| 21028 | .truncate => operand, | |
| 21029 | .round => try block.addUnOp(.round, operand), | |
| 21030 | .floor => try block.addUnOp(.floor, operand), | |
| 21031 | .ceil => try block.addUnOp(.ceil, operand), | |
| 21032 | .exact => unreachable, | |
| 21033 | }; | |
| 21034 | const air_cast_tag: Air.Inst.Tag = switch (block.float_mode) { | |
| 21035 | .optimized => if (safe) .int_from_float_optimized_safe else .int_from_float_optimized, | |
| 21036 | .strict => if (safe) .int_from_float_safe else .int_from_float, | |
| 21037 | }; | |
| 21038 | return block.addTyOp(air_cast_tag, dest_ty, uncasted_result); | |
| 21039 | } | |
| 21040 | ||
| 20903 | 21041 | fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 20904 | 21042 | const pt = sema.pt; |
| 20905 | 21043 | const zcu = pt.zcu; |
| ... | ... | @@ -25078,6 +25216,23 @@ fn zirFloatOpResultType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended. |
| 25078 | 25216 | return .fromType(float_ty); |
| 25079 | 25217 | } |
| 25080 | 25218 | |
| 25219 | fn zirRoundOpType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { | |
| 25220 | const pt = sema.pt; | |
| 25221 | const zcu = pt.zcu; | |
| 25222 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; | |
| 25223 | const operand_src = block.builtinCallArgSrc(extra.node, 0); | |
| 25224 | ||
| 25225 | const dest_ty = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse { | |
| 25226 | return .generic_poison_type; | |
| 25227 | }; | |
| 25228 | ||
| 25229 | const float_ty = dest_ty.optEuBaseType(zcu); | |
| 25230 | switch (float_ty.scalarType(zcu).zigTypeTag(zcu)) { | |
| 25231 | .float, .comptime_float => return .fromType(float_ty), | |
| 25232 | else => return .comptime_float_type, | |
| 25233 | } | |
| 25234 | } | |
| 25235 | ||
| 25081 | 25236 | fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { |
| 25082 | 25237 | if (block.isComptime()) { |
| 25083 | 25238 | const msg, const fail_block = msg: { |
| ... | ... | @@ -33467,7 +33622,7 @@ fn structFieldIndex( |
| 33467 | 33622 | return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); |
| 33468 | 33623 | } |
| 33469 | 33624 | |
| 33470 | const IntFromFloatMode = enum { exact, truncate }; | |
| 33625 | const IntFromFloatMode = enum { exact, truncate, round, floor, ceil }; | |
| 33471 | 33626 | |
| 33472 | 33627 | fn intFromFloat( |
| 33473 | 33628 | sema: *Sema, |
| ... | ... | @@ -33505,7 +33660,14 @@ fn intFromFloatScalar( |
| 33505 | 33660 | |
| 33506 | 33661 | if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, vec_idx); |
| 33507 | 33662 | |
| 33508 | const float = val.toFloat(f128, zcu); | |
| 33663 | var float = val.toFloat(f128, zcu); | |
| 33664 | switch (mode) { | |
| 33665 | .round => float = @round(float), | |
| 33666 | .floor => float = @floor(float), | |
| 33667 | .ceil => float = @ceil(float), | |
| 33668 | .truncate, .exact => {}, | |
| 33669 | } | |
| 33670 | ||
| 33509 | 33671 | if (std.math.isNan(float)) { |
| 33510 | 33672 | return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{ |
| 33511 | 33673 | int_ty.fmt(pt), |
| ... | ... | @@ -33530,7 +33692,7 @@ fn intFromFloatScalar( |
| 33530 | 33692 | "fractional component prevents float value '{f}' from coercion to type '{f}'", |
| 33531 | 33693 | .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) }, |
| 33532 | 33694 | ), |
| 33533 | .truncate => {}, | |
| 33695 | .truncate, .round, .floor, .ceil => {}, | |
| 33534 | 33696 | }, |
| 33535 | 33697 | .exact => {}, |
| 33536 | 33698 | } |
src/print_zir.zig+12| ... | ... | @@ -570,6 +570,7 @@ const Writer = struct { |
| 570 | 570 | .float_op_result_ty, |
| 571 | 571 | .reify_tuple, |
| 572 | 572 | .reify_pointer_sentinel_ty, |
| 573 | .round_op_ty, | |
| 573 | 574 | => { |
| 574 | 575 | const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 575 | 576 | try self.writeInstRef(stream, inst_data.operand); |
| ... | ... | @@ -593,6 +594,17 @@ const Writer = struct { |
| 593 | 594 | try self.writeSrcNode(stream, inst_data.node); |
| 594 | 595 | }, |
| 595 | 596 | |
| 597 | .round_op => { | |
| 598 | const round_op: Zir.Inst.RoundOp = @enumFromInt(extended.small); | |
| 599 | const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; | |
| 600 | try stream.print("{s}, ", .{@tagName(round_op)}); | |
| 601 | try self.writeInstRef(stream, inst_data.lhs); | |
| 602 | try stream.writeAll(", "); | |
| 603 | try self.writeInstRef(stream, inst_data.rhs); | |
| 604 | try stream.writeAll(")) "); | |
| 605 | try self.writeSrcNode(stream, inst_data.node); | |
| 606 | }, | |
| 607 | ||
| 596 | 608 | .reify_slice_arg_ty => { |
| 597 | 609 | const reify_slice_arg_info: Zir.Inst.ReifySliceArgInfo = @enumFromInt(extended.small); |
| 598 | 610 | const extra = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
test/behavior/cast.zig+64| ... | ... | @@ -116,6 +116,10 @@ test "@floatFromInt" { |
| 116 | 116 | const f = @as(f32, @floatFromInt(k)); |
| 117 | 117 | const i = @as(i32, @intFromFloat(f)); |
| 118 | 118 | try expect(i == k); |
| 119 | try expect(@as(i32, @round(f)) == k); | |
| 120 | try expect(@as(i32, @floor(f)) == k); | |
| 121 | try expect(@as(i32, @ceil(f)) == k); | |
| 122 | try expect(@as(i32, @trunc(f)) == k); | |
| 119 | 123 | } |
| 120 | 124 | }; |
| 121 | 125 | try S.doTheTest(); |
| ... | ... | @@ -139,6 +143,10 @@ test "@floatFromInt(f80)" { |
| 139 | 143 | const f = @as(f80, @floatFromInt(k)); |
| 140 | 144 | const i = @as(Int, @intFromFloat(f)); |
| 141 | 145 | try expect(i == k); |
| 146 | try expect(@as(Int, @round(f)) == k); | |
| 147 | try expect(@as(Int, @floor(f)) == k); | |
| 148 | try expect(@as(Int, @ceil(f)) == k); | |
| 149 | try expect(@as(Int, @trunc(f)) == k); | |
| 142 | 150 | } |
| 143 | 151 | }; |
| 144 | 152 | try S.doTheTest(i31); |
| ... | ... | @@ -167,6 +175,10 @@ test "type coercion from int to float" { |
| 167 | 175 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(float))); |
| 168 | 176 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(@ceil(float)))); |
| 169 | 177 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(@floor(float)))); |
| 178 | try std.testing.expectEqual(int, @as(Int, @round(float))); | |
| 179 | try std.testing.expectEqual(int, @as(Int, @ceil(float))); | |
| 180 | try std.testing.expectEqual(int, @as(Int, @floor(float))); | |
| 181 | try std.testing.expectEqual(int, @as(Int, @trunc(float))); | |
| 170 | 182 | } |
| 171 | 183 | |
| 172 | 184 | // Exhaustively check that all possible values of the integer type can |
| ... | ... | @@ -230,12 +242,54 @@ fn testIntFromFloats() !void { |
| 230 | 242 | try expectIntFromFloat(f32, 255.1, u8, 255); |
| 231 | 243 | try expectIntFromFloat(f32, 127.2, i8, 127); |
| 232 | 244 | try expectIntFromFloat(f32, -128.2, i8, -128); |
| 245 | ||
| 246 | try expectRoundCast(f32, 255.1, u8, 255); | |
| 247 | try expectFloorCast(f32, 255.1, u8, 255); | |
| 248 | try expectTruncCast(f32, 255.1, u8, 255); | |
| 249 | ||
| 250 | try expectRoundCast(f32, 127.2, i8, 127); | |
| 251 | try expectFloorCast(f32, 127.2, i8, 127); | |
| 252 | try expectTruncCast(f32, 127.2, i8, 127); | |
| 253 | ||
| 254 | try expectRoundCast(f32, -128.2, i8, -128); | |
| 255 | try expectCeilCast(f32, -128.2, i8, -128); | |
| 256 | try expectTruncCast(f32, -128.2, i8, -128); | |
| 257 | } | |
| 258 | ||
| 259 | test "rounding builtins with anytype and context propagation" { | |
| 260 | const S = struct { | |
| 261 | const x: i32 = 10; | |
| 262 | fn check(expected: anytype, actual: anytype) !void { | |
| 263 | try expectEqual(expected, actual); | |
| 264 | } | |
| 265 | }; | |
| 266 | try expectEqual(@as(f32, 1.0), @round(@as(f32, 1.4))); | |
| 267 | try S.check(@as(f32, 1.0), @round(@as(f32, 1.4))); | |
| 268 | ||
| 269 | const y: f64 = @floor(@floatFromInt(S.x)); | |
| 270 | try expect(y == 10.0); | |
| 271 | ||
| 272 | try expectEqual(1.0, @round(1.4)); | |
| 273 | try S.check(1.0, @round(1.4)); | |
| 233 | 274 | } |
| 234 | 275 | |
| 235 | 276 | fn expectIntFromFloat(comptime F: type, f: F, comptime I: type, i: I) !void { |
| 236 | 277 | try expect(@as(I, @intFromFloat(f)) == i); |
| 237 | 278 | } |
| 238 | 279 | |
| 280 | fn expectRoundCast(comptime F: type, f: F, comptime I: type, i: I) !void { | |
| 281 | try expect(@as(I, @round(f)) == i); | |
| 282 | } | |
| 283 | fn expectFloorCast(comptime F: type, f: F, comptime I: type, i: I) !void { | |
| 284 | try expect(@as(I, @floor(f)) == i); | |
| 285 | } | |
| 286 | fn expectCeilCast(comptime F: type, f: F, comptime I: type, i: I) !void { | |
| 287 | try expect(@as(I, @ceil(f)) == i); | |
| 288 | } | |
| 289 | fn expectTruncCast(comptime F: type, f: F, comptime I: type, i: I) !void { | |
| 290 | try expect(@as(I, @trunc(f)) == i); | |
| 291 | } | |
| 292 | ||
| 239 | 293 | test "implicitly cast indirect pointer to maybe-indirect pointer" { |
| 240 | 294 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 241 | 295 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| ... | ... | @@ -1309,6 +1363,12 @@ test "comptime float casts" { |
| 1309 | 1363 | |
| 1310 | 1364 | try expectIntFromFloat(comptime_int, 1234, i16, 1234); |
| 1311 | 1365 | try expectIntFromFloat(comptime_float, 12.3, comptime_int, 12); |
| 1366 | ||
| 1367 | try expectRoundCast(comptime_float, 12.3, comptime_int, 12); | |
| 1368 | ||
| 1369 | try expectFloorCast(comptime_float, 12.3, comptime_int, 12); | |
| 1370 | try expectCeilCast(comptime_float, 12.3, comptime_int, 13); | |
| 1371 | try expectTruncCast(comptime_float, 12.3, comptime_int, 12); | |
| 1312 | 1372 | } |
| 1313 | 1373 | |
| 1314 | 1374 | test "pointer reinterpret const float to int" { |
| ... | ... | @@ -1756,6 +1816,10 @@ test "intFromFloat to zero-bit int" { |
| 1756 | 1816 | |
| 1757 | 1817 | const a: f32 = 0.0; |
| 1758 | 1818 | try comptime std.testing.expect(@as(u0, @intFromFloat(a)) == 0); |
| 1819 | try comptime std.testing.expect(@as(u0, @round(a)) == 0); | |
| 1820 | try comptime std.testing.expect(@as(u0, @floor(a)) == 0); | |
| 1821 | try comptime std.testing.expect(@as(u0, @ceil(a)) == 0); | |
| 1822 | try comptime std.testing.expect(@as(u0, @trunc(a)) == 0); | |
| 1759 | 1823 | } |
| 1760 | 1824 | |
| 1761 | 1825 | test "peer type resolution of function pointer and function body" { |