| author | |
| committer | |
| log | 77919ee735706e7ebe0473fa9c3c9186df32c860 |
| tree | b60593ea7f86e49b3708acc2471c4d8faf31006a |
| parent | 2207c62bb5f6d61fc9f430e849d0aed13cf63d35 |
This effectively renders @intFromFloat redundant, as it is semantically
equivalent to @trunc when used in a context expecting an integer.5 files changed, 256 insertions(+), 7 deletions(-)
lib/std/zig/AstGen.zig+41-4| ... | @@ -9364,10 +9364,10 @@ fn builtinCall( | ... | @@ -9364,10 +9364,10 @@ fn builtinCall( |
| 9364 | .log => return floatUnOp(gz, scope, ri, node, params[0], .log), | 9364 | .log => return floatUnOp(gz, scope, ri, node, params[0], .log), |
| 9365 | .log2 => return floatUnOp(gz, scope, ri, node, params[0], .log2), | 9365 | .log2 => return floatUnOp(gz, scope, ri, node, params[0], .log2), |
| 9366 | .log10 => return floatUnOp(gz, scope, ri, node, params[0], .log10), | 9366 | .log10 => return floatUnOp(gz, scope, ri, node, params[0], .log10), |
| 9367 | .floor => return floatUnOp(gz, scope, ri, node, params[0], .floor), | 9367 | .floor => return floatRoundOp(gz, scope, ri, node, params[0], .floor), |
| 9368 | .ceil => return floatUnOp(gz, scope, ri, node, params[0], .ceil), | 9368 | .ceil => return floatRoundOp(gz, scope, ri, node, params[0], .ceil), |
| 9369 | .trunc => return floatUnOp(gz, scope, ri, node, params[0], .trunc), | 9369 | .trunc => return floatRoundOp(gz, scope, ri, node, params[0], .trunc), |
| 9370 | .round => return floatUnOp(gz, scope, ri, node, params[0], .round), | 9370 | .round => return floatRoundOp(gz, scope, ri, node, params[0], .round), |
| 9371 | 9371 | ||
| 9372 | .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name), | 9372 | .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name), |
| 9373 | .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name), | 9373 | .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name), |
| ... | @@ -9917,6 +9917,43 @@ fn simpleUnOp( | ... | @@ -9917,6 +9917,43 @@ fn simpleUnOp( |
| 9917 | return rvalue(gz, ri, result, node); | 9917 | return rvalue(gz, ri, result, node); |
| 9918 | } | 9918 | } |
| 9919 | 9919 | ||
| 9920 | fn floatRoundOp( | ||
| 9921 | gz: *GenZir, | ||
| 9922 | scope: *Scope, | ||
| 9923 | ri: ResultInfo, | ||
| 9924 | node: Ast.Node.Index, | ||
| 9925 | operand_node: Ast.Node.Index, | ||
| 9926 | float_tag: Zir.Inst.Tag, | ||
| 9927 | ) InnerError!Zir.Inst.Ref { | ||
| 9928 | if (try ri.rl.resultType(gz, node)) |dest_type| { | ||
| 9929 | const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); | ||
| 9930 | |||
| 9931 | const operand_ty_inst = try gz.addExtendedPayload(.round_op_ty, Zir.Inst.UnNode{ | ||
| 9932 | .node = gz.nodeIndexToRelative(node), | ||
| 9933 | .operand = dest_type, | ||
| 9934 | }); | ||
| 9935 | |||
| 9936 | const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = operand_ty_inst } }, operand_node); | ||
| 9937 | |||
| 9938 | try emitDbgStmt(gz, cursor); | ||
| 9939 | const cast_tag: Zir.Inst.Extended = switch (float_tag) { | ||
| 9940 | .round => .round_cast, | ||
| 9941 | .floor => .floor_cast, | ||
| 9942 | .ceil => .ceil_cast, | ||
| 9943 | .trunc => .trunc_cast, | ||
| 9944 | else => unreachable, | ||
| 9945 | }; | ||
| 9946 | const result = try gz.addExtendedPayload(cast_tag, Zir.Inst.BinNode{ | ||
| 9947 | .node = gz.nodeIndexToRelative(node), | ||
| 9948 | .lhs = dest_type, | ||
| 9949 | .rhs = operand, | ||
| 9950 | }); | ||
| 9951 | return rvalue(gz, ri, result, node); | ||
| 9952 | } else { | ||
| 9953 | return floatUnOp(gz, scope, ri, node, operand_node, float_tag); | ||
| 9954 | } | ||
| 9955 | } | ||
| 9956 | |||
| 9920 | fn floatUnOp( | 9957 | fn floatUnOp( |
| 9921 | gz: *GenZir, | 9958 | gz: *GenZir, |
| 9922 | scope: *Scope, | 9959 | scope: *Scope, |
lib/std/zig/Zir.zig+25| ... | @@ -2009,6 +2009,26 @@ pub const Inst = struct { | ... | @@ -2009,6 +2009,26 @@ pub const Inst = struct { |
| 2009 | /// `operand` is payload index to `BinNode`. | 2009 | /// `operand` is payload index to `BinNode`. |
| 2010 | /// `small` is unused. | 2010 | /// `small` is unused. |
| 2011 | shl_with_overflow, | 2011 | shl_with_overflow, |
| 2012 | /// Explicit rounding cast. | ||
| 2013 | /// `operand` is payload index to `Bin`. | ||
| 2014 | /// `small` is unused. | ||
| 2015 | round_cast, | ||
| 2016 | /// Explicit floor cast. | ||
| 2017 | /// `operand` is payload index to `Bin`. | ||
| 2018 | /// `small` is unused. | ||
| 2019 | floor_cast, | ||
| 2020 | /// Explicit ceil cast. | ||
| 2021 | /// `operand` is payload index to `Bin`. | ||
| 2022 | /// `small` is unused. | ||
| 2023 | ceil_cast, | ||
| 2024 | /// Explicit trunc cast. | ||
| 2025 | /// `operand` is payload index to `Bin`. | ||
| 2026 | /// `small` is unused. | ||
| 2027 | trunc_cast, | ||
| 2028 | /// Returns the type for the operand of a rounding op. | ||
| 2029 | /// `operand` is `UnNode`. | ||
| 2030 | /// `small` is unused. | ||
| 2031 | round_op_ty, | ||
| 2012 | /// `operand` is payload index to `UnNode`. | 2032 | /// `operand` is payload index to `UnNode`. |
| 2013 | c_undef, | 2033 | c_undef, |
| 2014 | /// `operand` is payload index to `UnNode`. | 2034 | /// `operand` is payload index to `UnNode`. |
| ... | @@ -4474,6 +4494,10 @@ fn findTrackableInner( | ... | @@ -4474,6 +4494,10 @@ fn findTrackableInner( |
| 4474 | .sub_with_overflow, | 4494 | .sub_with_overflow, |
| 4475 | .mul_with_overflow, | 4495 | .mul_with_overflow, |
| 4476 | .shl_with_overflow, | 4496 | .shl_with_overflow, |
| 4497 | .round_cast, | ||
| 4498 | .floor_cast, | ||
| 4499 | .ceil_cast, | ||
| 4500 | .trunc_cast, | ||
| 4477 | .c_undef, | 4501 | .c_undef, |
| 4478 | .c_include, | 4502 | .c_include, |
| 4479 | .c_define, | 4503 | .c_define, |
| ... | @@ -4515,6 +4539,7 @@ fn findTrackableInner( | ... | @@ -4515,6 +4539,7 @@ fn findTrackableInner( |
| 4515 | .dbg_empty_stmt, | 4539 | .dbg_empty_stmt, |
| 4516 | .astgen_error, | 4540 | .astgen_error, |
| 4517 | .float_op_result_ty, | 4541 | .float_op_result_ty, |
| 4542 | .round_op_ty, | ||
| 4518 | => return, | 4543 | => return, |
| 4519 | 4544 | ||
| 4520 | // `@TypeOf` has a body. | 4545 | // `@TypeOf` has a body. |
src/Sema.zig+137-3| ... | @@ -1398,6 +1398,11 @@ fn analyzeBodyInner( | ... | @@ -1398,6 +1398,11 @@ fn analyzeBodyInner( |
| 1398 | .@"asm" => try sema.zirAsm( block, extended, false), | 1398 | .@"asm" => try sema.zirAsm( block, extended, false), |
| 1399 | .asm_expr => try sema.zirAsm( block, extended, true), | 1399 | .asm_expr => try sema.zirAsm( block, extended, true), |
| 1400 | .typeof_peer => try sema.zirTypeofPeer( block, extended, inst), | 1400 | .typeof_peer => try sema.zirTypeofPeer( block, extended, inst), |
| 1401 | .round_cast => try sema.zirRoundCast( block, extended, .round), | ||
| 1402 | .floor_cast => try sema.zirRoundCast( block, extended, .floor), | ||
| 1403 | .ceil_cast => try sema.zirRoundCast( block, extended, .ceil), | ||
| 1404 | .trunc_cast => try sema.zirRoundCast( block, extended, .truncate), | ||
| 1405 | .round_op_ty => try sema.zirRoundOpType( block, extended), | ||
| 1401 | .compile_log => try sema.zirCompileLog( block, extended), | 1406 | .compile_log => try sema.zirCompileLog( block, extended), |
| 1402 | .min_multi => try sema.zirMinMaxMulti( block, extended, .min), | 1407 | .min_multi => try sema.zirMinMaxMulti( block, extended, .min), |
| 1403 | .max_multi => try sema.zirMinMaxMulti( block, extended, .max), | 1408 | .max_multi => try sema.zirMinMaxMulti( block, extended, .max), |
| ... | @@ -21647,6 +21652,113 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro | ... | @@ -21647,6 +21652,113 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro |
| 21647 | }, dest_ty, operand); | 21652 | }, dest_ty, operand); |
| 21648 | } | 21653 | } |
| 21649 | 21654 | ||
| 21655 | fn zirRoundCast( | ||
| 21656 | sema: *Sema, | ||
| 21657 | block: *Block, | ||
| 21658 | extended: Zir.Inst.Extended.InstData, | ||
| 21659 | mode: IntFromFloatMode, | ||
| 21660 | ) CompileError!Air.Inst.Ref { | ||
| 21661 | const pt = sema.pt; | ||
| 21662 | const zcu = pt.zcu; | ||
| 21663 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; | ||
| 21664 | const src = block.nodeOffset(extra.node); | ||
| 21665 | const operand_src = block.builtinCallArgSrc(extra.node, 0); | ||
| 21666 | |||
| 21667 | const builtin_name = switch (mode) { | ||
| 21668 | .round => "@round", | ||
| 21669 | .floor => "@floor", | ||
| 21670 | .ceil => "@ceil", | ||
| 21671 | .truncate => "@trunc", | ||
| 21672 | .exact => unreachable, | ||
| 21673 | }; | ||
| 21674 | |||
| 21675 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, builtin_name); | ||
| 21676 | const operand = try sema.resolveInst(extra.rhs); | ||
| 21677 | const operand_ty = sema.typeOf(operand); | ||
| 21678 | |||
| 21679 | try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); | ||
| 21680 | const dest_scalar_ty = dest_ty.scalarType(zcu); | ||
| 21681 | const operand_scalar_ty = operand_ty.scalarType(zcu); | ||
| 21682 | |||
| 21683 | if (dest_scalar_ty.zigTypeTag(zcu) == .float or dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) { | ||
| 21684 | const coerced_operand = try sema.coerce(block, dest_ty, operand, operand_src); | ||
| 21685 | |||
| 21686 | const result_ref = switch (mode) { | ||
| 21687 | .round => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.round), | ||
| 21688 | .floor => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.floor), | ||
| 21689 | .ceil => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.ceil), | ||
| 21690 | .truncate => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.trunc), | ||
| 21691 | else => unreachable, | ||
| 21692 | }; | ||
| 21693 | |||
| 21694 | if (result_ref) |ref| return ref; | ||
| 21695 | |||
| 21696 | const air_tag: Air.Inst.Tag = switch (mode) { | ||
| 21697 | .round => .round, | ||
| 21698 | .floor => .floor, | ||
| 21699 | .ceil => .ceil, | ||
| 21700 | .truncate => .trunc_float, | ||
| 21701 | else => unreachable, | ||
| 21702 | }; | ||
| 21703 | |||
| 21704 | try sema.requireRuntimeBlock(block, operand_src, null); | ||
| 21705 | return block.addUnOp(air_tag, coerced_operand); | ||
| 21706 | } | ||
| 21707 | |||
| 21708 | _ = try sema.checkIntType(block, src, dest_scalar_ty); | ||
| 21709 | try sema.checkFloatType(block, operand_src, operand_scalar_ty); | ||
| 21710 | |||
| 21711 | if (try sema.resolveValue(operand)) |operand_val| { | ||
| 21712 | const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, mode); | ||
| 21713 | return Air.internedToRef(result_val.toIntern()); | ||
| 21714 | } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { | ||
| 21715 | return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int }); | ||
| 21716 | } | ||
| 21717 | |||
| 21718 | try sema.requireRuntimeBlock(block, src, operand_src); | ||
| 21719 | |||
| 21720 | if (dest_scalar_ty.intInfo(zcu).bits == 0) { | ||
| 21721 | if (block.wantSafety()) { | ||
| 21722 | const abs_ref = try block.addTyOp(.abs, operand_ty, operand); | ||
| 21723 | const is_vector = dest_ty.zigTypeTag(zcu) == .vector; | ||
| 21724 | const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref; | ||
| 21725 | const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern()); | ||
| 21726 | const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref); | ||
| 21727 | try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds); | ||
| 21728 | } | ||
| 21729 | const scalar_val = try pt.intValue(dest_scalar_ty, 0); | ||
| 21730 | return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern()); | ||
| 21731 | } | ||
| 21732 | |||
| 21733 | const safe = block.wantSafety(); | ||
| 21734 | const optimized = block.float_mode == .optimized; | ||
| 21735 | |||
| 21736 | if (safe) { | ||
| 21737 | try sema.preparePanicId(src, .integer_part_out_of_bounds); | ||
| 21738 | } | ||
| 21739 | |||
| 21740 | if (mode == .truncate) { | ||
| 21741 | const air_tag: Air.Inst.Tag = if (safe) | ||
| 21742 | if (optimized) .int_from_float_optimized_safe else .int_from_float_safe | ||
| 21743 | else if (optimized) .int_from_float_optimized else .int_from_float; | ||
| 21744 | return block.addTyOp(air_tag, dest_ty, operand); | ||
| 21745 | } else { | ||
| 21746 | const op_tag: Air.Inst.Tag = switch (mode) { | ||
| 21747 | .round => .round, | ||
| 21748 | .floor => .floor, | ||
| 21749 | .ceil => .ceil, | ||
| 21750 | else => unreachable, | ||
| 21751 | }; | ||
| 21752 | const rounded_op = try block.addUnOp(op_tag, operand); | ||
| 21753 | |||
| 21754 | const air_tag: Air.Inst.Tag = if (safe) | ||
| 21755 | if (optimized) .int_from_float_optimized_safe else .int_from_float_safe | ||
| 21756 | else if (optimized) .int_from_float_optimized else .int_from_float; | ||
| 21757 | |||
| 21758 | return block.addTyOp(air_tag, dest_ty, rounded_op); | ||
| 21759 | } | ||
| 21760 | } | ||
| 21761 | |||
| 21650 | fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 21762 | fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 21651 | const pt = sema.pt; | 21763 | const pt = sema.pt; |
| 21652 | const zcu = pt.zcu; | 21764 | const zcu = pt.zcu; |
| ... | @@ -25804,6 +25916,21 @@ fn zirFloatOpResultType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended. | ... | @@ -25804,6 +25916,21 @@ fn zirFloatOpResultType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended. |
| 25804 | return .fromType(float_ty); | 25916 | return .fromType(float_ty); |
| 25805 | } | 25917 | } |
| 25806 | 25918 | ||
| 25919 | fn zirRoundOpType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { | ||
| 25920 | const pt = sema.pt; | ||
| 25921 | const zcu = pt.zcu; | ||
| 25922 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; | ||
| 25923 | const operand_src = block.builtinCallArgSrc(extra.node, 0); | ||
| 25924 | |||
| 25925 | const dest_ty = try sema.resolveDestType(block, operand_src, extra.operand, .remove_eu_opt, "type hint"); | ||
| 25926 | |||
| 25927 | const float_ty = dest_ty.optEuBaseType(zcu); | ||
| 25928 | switch (float_ty.scalarType(zcu).zigTypeTag(zcu)) { | ||
| 25929 | .float, .comptime_float => return .fromType(float_ty), | ||
| 25930 | else => return .comptime_float_type, | ||
| 25931 | } | ||
| 25932 | } | ||
| 25933 | |||
| 25807 | fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { | 25934 | fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { |
| 25808 | if (block.isComptime()) { | 25935 | if (block.isComptime()) { |
| 25809 | const msg, const fail_block = msg: { | 25936 | const msg, const fail_block = msg: { |
| ... | @@ -36413,7 +36540,7 @@ fn structFieldIndex( | ... | @@ -36413,7 +36540,7 @@ fn structFieldIndex( |
| 36413 | return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); | 36540 | return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); |
| 36414 | } | 36541 | } |
| 36415 | 36542 | ||
| 36416 | const IntFromFloatMode = enum { exact, truncate }; | 36543 | const IntFromFloatMode = enum { exact, truncate, round, floor, ceil }; |
| 36417 | 36544 | ||
| 36418 | fn intFromFloat( | 36545 | fn intFromFloat( |
| 36419 | sema: *Sema, | 36546 | sema: *Sema, |
| ... | @@ -36451,7 +36578,14 @@ fn intFromFloatScalar( | ... | @@ -36451,7 +36578,14 @@ fn intFromFloatScalar( |
| 36451 | 36578 | ||
| 36452 | if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, vec_idx); | 36579 | if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, vec_idx); |
| 36453 | 36580 | ||
| 36454 | const float = val.toFloat(f128, zcu); | 36581 | var float = val.toFloat(f128, zcu); |
| 36582 | switch (mode) { | ||
| 36583 | .round => float = @round(float), | ||
| 36584 | .floor => float = @floor(float), | ||
| 36585 | .ceil => float = @ceil(float), | ||
| 36586 | .truncate, .exact => {}, | ||
| 36587 | } | ||
| 36588 | |||
| 36455 | if (std.math.isNan(float)) { | 36589 | if (std.math.isNan(float)) { |
| 36456 | return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{ | 36590 | return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{ |
| 36457 | int_ty.fmt(pt), | 36591 | int_ty.fmt(pt), |
| ... | @@ -36476,7 +36610,7 @@ fn intFromFloatScalar( | ... | @@ -36476,7 +36610,7 @@ fn intFromFloatScalar( |
| 36476 | "fractional component prevents float value '{f}' from coercion to type '{f}'", | 36610 | "fractional component prevents float value '{f}' from coercion to type '{f}'", |
| 36477 | .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) }, | 36611 | .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) }, |
| 36478 | ), | 36612 | ), |
| 36479 | .truncate => {}, | 36613 | .truncate, .round, .floor, .ceil => {}, |
| 36480 | }, | 36614 | }, |
| 36481 | .exact => {}, | 36615 | .exact => {}, |
| 36482 | } | 36616 | } |
src/print_zir.zig+5| ... | @@ -570,6 +570,7 @@ const Writer = struct { | ... | @@ -570,6 +570,7 @@ const Writer = struct { |
| 570 | .float_op_result_ty, | 570 | .float_op_result_ty, |
| 571 | .reify_tuple, | 571 | .reify_tuple, |
| 572 | .reify_pointer_sentinel_ty, | 572 | .reify_pointer_sentinel_ty, |
| 573 | .round_op_ty, | ||
| 573 | => { | 574 | => { |
| 574 | const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; | 575 | const inst_data = self.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 575 | try self.writeInstRef(stream, inst_data.operand); | 576 | try self.writeInstRef(stream, inst_data.operand); |
| ... | @@ -584,6 +585,10 @@ const Writer = struct { | ... | @@ -584,6 +585,10 @@ const Writer = struct { |
| 584 | .prefetch, | 585 | .prefetch, |
| 585 | .c_va_arg, | 586 | .c_va_arg, |
| 586 | .reify_enum_value_slice_ty, | 587 | .reify_enum_value_slice_ty, |
| 588 | .round_cast, | ||
| 589 | .floor_cast, | ||
| 590 | .ceil_cast, | ||
| 591 | .trunc_cast, | ||
| 587 | => { | 592 | => { |
| 588 | const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; | 593 | const inst_data = self.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 589 | try self.writeInstRef(stream, inst_data.lhs); | 594 | try self.writeInstRef(stream, inst_data.lhs); |
test/behavior/cast.zig+48| ... | @@ -116,6 +116,10 @@ test "@floatFromInt" { | ... | @@ -116,6 +116,10 @@ test "@floatFromInt" { |
| 116 | const f = @as(f32, @floatFromInt(k)); | 116 | const f = @as(f32, @floatFromInt(k)); |
| 117 | const i = @as(i32, @intFromFloat(f)); | 117 | const i = @as(i32, @intFromFloat(f)); |
| 118 | try expect(i == k); | 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 | try S.doTheTest(); | 125 | try S.doTheTest(); |
| ... | @@ -139,6 +143,10 @@ test "@floatFromInt(f80)" { | ... | @@ -139,6 +143,10 @@ test "@floatFromInt(f80)" { |
| 139 | const f = @as(f80, @floatFromInt(k)); | 143 | const f = @as(f80, @floatFromInt(k)); |
| 140 | const i = @as(Int, @intFromFloat(f)); | 144 | const i = @as(Int, @intFromFloat(f)); |
| 141 | try expect(i == k); | 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 | try S.doTheTest(i31); | 152 | try S.doTheTest(i31); |
| ... | @@ -167,6 +175,10 @@ test "type coercion from int to float" { | ... | @@ -167,6 +175,10 @@ test "type coercion from int to float" { |
| 167 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(float))); | 175 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(float))); |
| 168 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(@ceil(float)))); | 176 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(@ceil(float)))); |
| 169 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(@floor(float)))); | 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 | // Exhaustively check that all possible values of the integer type can | 184 | // Exhaustively check that all possible values of the integer type can |
| ... | @@ -227,12 +239,37 @@ fn testIntFromFloats() !void { | ... | @@ -227,12 +239,37 @@ fn testIntFromFloats() !void { |
| 227 | try expectIntFromFloat(f32, 255.1, u8, 255); | 239 | try expectIntFromFloat(f32, 255.1, u8, 255); |
| 228 | try expectIntFromFloat(f32, 127.2, i8, 127); | 240 | try expectIntFromFloat(f32, 127.2, i8, 127); |
| 229 | try expectIntFromFloat(f32, -128.2, i8, -128); | 241 | try expectIntFromFloat(f32, -128.2, i8, -128); |
| 242 | |||
| 243 | try expectRoundCast(f32, 255.1, u8, 255); | ||
| 244 | try expectFloorCast(f32, 255.1, u8, 255); | ||
| 245 | try expectTruncCast(f32, 255.1, u8, 255); | ||
| 246 | |||
| 247 | try expectRoundCast(f32, 127.2, i8, 127); | ||
| 248 | try expectFloorCast(f32, 127.2, i8, 127); | ||
| 249 | try expectTruncCast(f32, 127.2, i8, 127); | ||
| 250 | |||
| 251 | try expectRoundCast(f32, -128.2, i8, -128); | ||
| 252 | try expectCeilCast(f32, -128.2, i8, -128); | ||
| 253 | try expectTruncCast(f32, -128.2, i8, -128); | ||
| 230 | } | 254 | } |
| 231 | 255 | ||
| 232 | fn expectIntFromFloat(comptime F: type, f: F, comptime I: type, i: I) !void { | 256 | fn expectIntFromFloat(comptime F: type, f: F, comptime I: type, i: I) !void { |
| 233 | try expect(@as(I, @intFromFloat(f)) == i); | 257 | try expect(@as(I, @intFromFloat(f)) == i); |
| 234 | } | 258 | } |
| 235 | 259 | ||
| 260 | fn expectRoundCast(comptime F: type, f: F, comptime I: type, i: I) !void { | ||
| 261 | try expect(@as(I, @round(f)) == i); | ||
| 262 | } | ||
| 263 | fn expectFloorCast(comptime F: type, f: F, comptime I: type, i: I) !void { | ||
| 264 | try expect(@as(I, @floor(f)) == i); | ||
| 265 | } | ||
| 266 | fn expectCeilCast(comptime F: type, f: F, comptime I: type, i: I) !void { | ||
| 267 | try expect(@as(I, @ceil(f)) == i); | ||
| 268 | } | ||
| 269 | fn expectTruncCast(comptime F: type, f: F, comptime I: type, i: I) !void { | ||
| 270 | try expect(@as(I, @trunc(f)) == i); | ||
| 271 | } | ||
| 272 | |||
| 236 | test "implicitly cast indirect pointer to maybe-indirect pointer" { | 273 | test "implicitly cast indirect pointer to maybe-indirect pointer" { |
| 237 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO | 274 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
| 238 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; | 275 | if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; |
| ... | @@ -1306,6 +1343,13 @@ test "comptime float casts" { | ... | @@ -1306,6 +1343,13 @@ test "comptime float casts" { |
| 1306 | 1343 | ||
| 1307 | try expectIntFromFloat(comptime_int, 1234, i16, 1234); | 1344 | try expectIntFromFloat(comptime_int, 1234, i16, 1234); |
| 1308 | try expectIntFromFloat(comptime_float, 12.3, comptime_int, 12); | 1345 | try expectIntFromFloat(comptime_float, 12.3, comptime_int, 12); |
| 1346 | |||
| 1347 | try expectRoundCast(comptime_int, 1234, i16, 1234); | ||
| 1348 | try expectRoundCast(comptime_float, 12.3, comptime_int, 12); | ||
| 1349 | |||
| 1350 | try expectFloorCast(comptime_float, 12.3, comptime_int, 12); | ||
| 1351 | try expectCeilCast(comptime_float, 12.3, comptime_int, 13); | ||
| 1352 | try expectTruncCast(comptime_float, 12.3, comptime_int, 12); | ||
| 1309 | } | 1353 | } |
| 1310 | 1354 | ||
| 1311 | test "pointer reinterpret const float to int" { | 1355 | test "pointer reinterpret const float to int" { |
| ... | @@ -1739,6 +1783,10 @@ test "intFromFloat to zero-bit int" { | ... | @@ -1739,6 +1783,10 @@ test "intFromFloat to zero-bit int" { |
| 1739 | 1783 | ||
| 1740 | const a: f32 = 0.0; | 1784 | const a: f32 = 0.0; |
| 1741 | try comptime std.testing.expect(@as(u0, @intFromFloat(a)) == 0); | 1785 | try comptime std.testing.expect(@as(u0, @intFromFloat(a)) == 0); |
| 1786 | try comptime std.testing.expect(@as(u0, @round(a)) == 0); | ||
| 1787 | try comptime std.testing.expect(@as(u0, @floor(a)) == 0); | ||
| 1788 | try comptime std.testing.expect(@as(u0, @ceil(a)) == 0); | ||
| 1789 | try comptime std.testing.expect(@as(u0, @trunc(a)) == 0); | ||
| 1742 | } | 1790 | } |
| 1743 | 1791 | ||
| 1744 | test "peer type resolution of function pointer and function body" { | 1792 | test "peer type resolution of function pointer and function body" { |