Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -1340,7 +1340,8 @@ a /= b{#endsyntax#}</pre></td>
<li>Can cause {#link|Division by Zero#} for floats in {#link|FloatMode.Optimized Mode|Floating Point Operations#}.</li>
<li>Signed integer operands must be comptime-known and positive. In other cases, use
{#link|@divTrunc#},
{#link|@divFloor#}, or
{#link|@divFloor#},
{#link|@divCeil#}, or
{#link|@divExact#} instead.
</li>
<li>Invokes {#link|Peer Type Resolution#} for the operands.</li>
Expand Down Expand Up @@ -4695,7 +4696,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<li>{#syntax#}@divExact(a, b) * b == a{#endsyntax#}</li>
</ul>
<p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divExact{#endsyntax#}.</p>
{#see_also|@divTrunc|@divFloor#}
{#see_also|@divTrunc|@divFloor|@divCeil#}
{#header_close#}
{#header_open|@divFloor#}
<pre>{#syntax#}@divFloor(numerator: T, denominator: T) T{#endsyntax#}</pre>
Expand All @@ -4709,7 +4710,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<li>{#syntax#}(@divFloor(a, b) * b) + @mod(a, b) == a{#endsyntax#}</li>
</ul>
<p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divFloor{#endsyntax#}.</p>
{#see_also|@divTrunc|@divExact#}
{#see_also|@divTrunc|@divCeil|@divExact#}
{#header_close#}
{#header_open|@divTrunc#}
<pre>{#syntax#}@divTrunc(numerator: T, denominator: T) T{#endsyntax#}</pre>
Expand All @@ -4723,7 +4724,20 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<li>{#syntax#}(@divTrunc(a, b) * b) + @rem(a, b) == a{#endsyntax#}</li>
</ul>
<p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divTrunc{#endsyntax#}.</p>
{#see_also|@divFloor|@divExact#}
{#see_also|@divFloor|@divCeil|@divExact#}
{#header_close#}
{#header_open|@divCeil#}
<pre>{#syntax#}@divCeil(numerator: T, denominator: T) T{#endsyntax#}</pre>
<p>
Ceiled division. Rounds toward positive infinity. Caller guarantees {#syntax#}denominator != 0{#endsyntax#} and
{#syntax#}!(@typeInfo(T) == .int and T.is_signed and numerator == std.math.minInt(T) and denominator == -1){#endsyntax#}.
</p>
<ul>
<li>{#syntax#}@divCeil(5, 3) == 2{#endsyntax#}</li>
<li>{#syntax#}@divCeil(-5, 3) == -1{#endsyntax#}</li>
</ul>
<p>For a function that returns a possible error code, use {#syntax#}@import("std").math.divCeil{#endsyntax#}.</p>
{#see_also|@divFloor|@divTrunc|@divExact#}
{#header_close#}

{#header_open|@embedFile#}
Expand Down Expand Up @@ -5963,6 +5977,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<li>{#syntax#}/{#endsyntax#} (division)</li>
<li>{#link|@divTrunc#} (division)</li>
<li>{#link|@divFloor#} (division)</li>
<li>{#link|@divCeil#} (division)</li>
<li>{#link|@divExact#} (division)</li>
</ul>
<p>Example with addition at compile-time:</p>
Expand All @@ -5980,6 +5995,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
<li>{#syntax#}@import("std").math.mul{#endsyntax#}</li>
<li>{#syntax#}@import("std").math.divTrunc{#endsyntax#}</li>
<li>{#syntax#}@import("std").math.divFloor{#endsyntax#}</li>
<li>{#syntax#}@import("std").math.divCeil{#endsyntax#}</li>
<li>{#syntax#}@import("std").math.divExact{#endsyntax#}</li>
<li>{#syntax#}@import("std").math.shl{#endsyntax#}</li>
</ul>
Expand Down
1 change: 1 addition & 0 deletions lib/std/math.zig
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ fn testDivFloor() !void {
/// infinity. Returns an error on overflow or when denominator is
/// zero.
pub fn divCeil(comptime T: type, numerator: T, denominator: T) !T {
// TODO: replace with @divCeil once https://github.com/ziglang/zig/pull/21757 is merged
@setRuntimeSafety(false);
if (denominator == 0) return error.DivisionByZero;
const info = @typeInfo(T);
Expand Down
66 changes: 66 additions & 0 deletions lib/std/math/big/int.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,72 @@ pub const Mutable = struct {
}
}

/// q = a / b (rem r)
///
/// a / b are ceiled (rounded towards +inf).
/// q may alias with a or b.
///
/// Asserts there is enough memory to store q and r.
/// The upper bound for r limb count is `b.limbs.len`.
/// The upper bound for q limb count is given by `a.limbs`.
///
/// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`.
pub fn divCeil(
q: *Mutable,
r: *Mutable,
a: Const,
b: Const,
limbs_buffer: []Limb,
) void {
const sep = a.limbs.len + 2;
var x = a.toMutable(limbs_buffer[0..sep]);
var y = b.toMutable(limbs_buffer[sep..]);

// div performs truncating division (@divTrunc) which rounds towards negative
// infinity if the result is positive and towards positive infinity if the result is
// negative.
div(q, r, &x, &y);

// @rem gives the remainder after @divTrunc, and is defined by:
// x * @divTrunc(x, y) + @rem(x, y) = x
// For all integers x, y with y != 0.
// In the following comments, a, b will be integers with a >= 0, b > 0, and we will take
// modCeil to be the remainder after @divCeil, defined by:
// x * @divCeil(x, y) + modCeil(x, y) = x
// For all integers x, y with y != 0.

if (a.positive != b.positive or r.eqlZero()) {
// In this case either the result is negative or the remainder is 0.
// If the result is negative then the default truncating division already rounds
// towards positive infinity, so no adjustment is needed.
// If the remainder is 0 then the division is exact and no adjustment is needed.
} else if (a.positive) {
// Both positive.
// We have:
// modCeil(a, b) != 0
// => @divCeil(a, b) = @divTrunc(a, b) + 1
// And:
// b * @divTrunc(a, b) + @rem(a, b) = a
// b * @divCeil(a, b) + modCeil(a, b) = a
// => b * @divTrunc(a, b) + b + modCeil(a, b) = a
// => modCeil(a, b) = @rem(a, b) - b
q.addScalar(q.toConst(), 1);
r.sub(r.toConst(), y.toConst());
} else {
// Both negative.
// We have:
// modCeil(-a, -b) != 0
// => @divCeil(-a, -b) = @divTrunc(-a, -b) + 1
// And:
// -b * @divTrunc(-a, -b) + @rem(-a, -b) = -a
// -b * @divCeil(-a, -b) + modCeil(-a, -b) = -a
// => -b * @divTrunc(-a, -b) - b + modCeil(-a, -b) = -a
// => modCeil(-a, -b) = @rem(-a, -b) + b
q.addScalar(q.toConst(), 1);
r.add(r.toConst(), y.toConst().abs());
}
}

/// q = a / b (rem r)
///
/// a / b are truncated (rounded towards -inf).
Expand Down
2 changes: 2 additions & 0 deletions lib/std/zig/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2905,6 +2905,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
.pop_count,
.byte_swap,
.bit_reverse,
.div_ceil,
.div_exact,
.div_floor,
.div_trunc,
Expand Down Expand Up @@ -9684,6 +9685,7 @@ fn builtinCall(
.byte_swap => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap),
.bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse),

.div_ceil => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_ceil),
.div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
.div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
.div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
Expand Down
1 change: 1 addition & 0 deletions lib/std/zig/AstRlAnnotate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -961,6 +961,7 @@ fn builtinCall(astrl: *AstRlAnnotate, block: ?*Block, ri: ResultInfo, node: Ast.
_ = try astrl.expr(args[0], block, ResultInfo.none);
return false;
},
.div_ceil,
.div_exact,
.div_floor,
.div_trunc,
Expand Down
8 changes: 8 additions & 0 deletions lib/std/zig/BuiltinFn.zig
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub const Tag = enum {
c_va_copy,
c_va_end,
c_va_start,
div_ceil,
div_exact,
div_floor,
div_trunc,
Expand Down Expand Up @@ -403,6 +404,13 @@ pub const list = list: {
.illegal_outside_function = true,
},
},
.{
"@divCeil",
.{
.tag = .div_ceil,
.param_count = 2,
},
},
.{
"@divExact",
.{
Expand Down
7 changes: 7 additions & 0 deletions lib/std/zig/Zir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ pub const Inst = struct {
/// Saturating multiplication.
/// Uses the `pl_node` union field. Payload is `Bin`.
mul_sat,
/// Implements the `@divCeil` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
div_ceil,
/// Implements the `@divExact` builtin.
/// Uses the `pl_node` union field with payload `Bin`.
div_exact,
Expand Down Expand Up @@ -1246,6 +1249,7 @@ pub const Inst = struct {
.pop_count,
.byte_swap,
.bit_reverse,
.div_ceil,
.div_exact,
.div_floor,
.div_trunc,
Expand Down Expand Up @@ -1532,6 +1536,7 @@ pub const Inst = struct {
.pop_count,
.byte_swap,
.bit_reverse,
.div_ceil,
.div_exact,
.div_floor,
.div_trunc,
Expand Down Expand Up @@ -1801,6 +1806,7 @@ pub const Inst = struct {
.byte_swap = .un_node,
.bit_reverse = .un_node,

.div_ceil = .pl_node,
.div_exact = .pl_node,
.div_floor = .pl_node,
.div_trunc = .pl_node,
Expand Down Expand Up @@ -4052,6 +4058,7 @@ fn findTrackableInner(
.mul,
.mulwrap,
.mul_sat,
.div_ceil,
.div_exact,
.div_floor,
.div_trunc,
Expand Down
35 changes: 35 additions & 0 deletions lib/zig.h
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,15 @@ typedef ptrdiff_t intptr_t;
static inline int##w##_t zig_div_floor_i##w(int##w##_t lhs, int##w##_t rhs) { \
return lhs / rhs + (lhs % rhs != INT##w##_C(0) ? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) : INT##w##_C(0)); \
} \
\
static inline uint##w##_t zig_div_ceil_u##w(uint##w##_t lhs, uint##w##_t rhs) { \
return lhs / rhs + (lhs % rhs != UINT##w##_C(0) ? UINT##w##_C(1) : UINT##w##_C(0)); \
} \
\
static inline int##w##_t zig_div_ceil_i##w(int##w##_t lhs, int##w##_t rhs) { \
return lhs / rhs + (lhs % rhs != INT##w##_C(0) \
? zig_shr_i##w(lhs ^ rhs, UINT8_C(w) - UINT8_C(1)) + INT##w##_C(1) : INT##w##_C(0)); \
} \
\
zig_basic_operator(uint##w##_t, mod_u##w, %) \
\
Expand Down Expand Up @@ -1600,6 +1609,21 @@ static inline zig_i128 zig_div_floor_i128(zig_i128 lhs, zig_i128 rhs) {
return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(mask, (uint64_t)mask));
}

static inline zig_u128 zig_div_ceil_u128(zig_u128 lhs, zig_u128 rhs) {
zig_u128 rem = zig_rem_u128(lhs, rhs);
uint64_t mask = zig_or_u64(zig_hi_u128(rem), zig_lo_u128(rem)) != UINT64_C(0)
? UINT64_C(1) : UINT64_C(0);
return zig_add_u128(zig_div_trunc_u128(lhs, rhs), zig_make_u128(UINT64_C(0), mask));
}

static inline zig_i128 zig_div_ceil_i128(zig_i128 lhs, zig_i128 rhs) {
zig_i128 rem = zig_rem_i128(lhs, rhs);
int64_t mask = zig_or_u64((uint64_t)zig_hi_i128(rem), zig_lo_i128(rem)) != UINT64_C(0)
? zig_shr_i64(zig_xor_i64(zig_hi_i128(lhs), zig_hi_i128(rhs)), UINT8_C(63)) + INT64_C(1)
: INT64_C(0);
return zig_add_i128(zig_div_trunc_i128(lhs, rhs), zig_make_i128(INT64_C(0), (uint64_t)mask));
}

#define zig_mod_u128 zig_rem_u128

static inline zig_i128 zig_mod_i128(zig_i128 lhs, zig_i128 rhs) {
Expand Down Expand Up @@ -2775,6 +2799,13 @@ static inline void zig_div_floor_big(void *res, const void *lhs, const void *rhs
zig_trap();
}

static inline void zig_div_ceil_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
// TODO: need another wrapper for divmod from lib/compiler_rt/udivmodei4.zig
// but is this even needed?

zig_trap();
}

zig_extern void __umodei4(uint32_t *res, const uint32_t *lhs, const uint32_t *rhs, uintptr_t bits);
static inline void zig_rem_big(void *res, const void *lhs, const void *rhs, bool is_signed, uint16_t bits) {
if (!is_signed) {
Expand Down Expand Up @@ -3534,6 +3565,10 @@ zig_float_negate_builtin(128, zig_make_u128, (UINT64_C(1) << 63, UINT64_C(0)))
static inline zig_f##w zig_div_floor_f##w(zig_f##w lhs, zig_f##w rhs) { \
return zig_floor_f##w(zig_div_f##w(lhs, rhs)); \
} \
\
static inline zig_f##w zig_div_ceil_f##w(zig_f##w lhs, zig_f##w rhs) { \
return zig_ceil_f##w(zig_div_f##w(lhs, rhs)); \
} \
\
static inline zig_f##w zig_mod_f##w(zig_f##w lhs, zig_f##w rhs) { \
return zig_sub_f##w(lhs, zig_mul_f##w(zig_div_floor_f##w(lhs, rhs), rhs)); \
Expand Down
11 changes: 11 additions & 0 deletions src/Air.zig
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ pub const Inst = struct {
div_floor,
/// Same as `div_floor` with optimized float mode.
div_floor_optimized,
/// Ceiling integer or float division. For integers, wrapping is undefined behavior.
/// Both operands are guaranteed to be the same type, and the result type
/// is the same as both operands.
/// Uses the `bin_op` field.
div_ceil,
/// Same as `div_ceil` with optimized float mode.
div_ceil_optimized,
/// Integer or float division.
/// If a remainder would be produced, undefined behavior occurs.
/// For integers, overflow is undefined behavior.
Expand Down Expand Up @@ -1333,6 +1340,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
.div_float,
.div_trunc,
.div_floor,
.div_ceil,
.div_exact,
.rem,
.mod,
Expand All @@ -1354,6 +1362,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
.div_float_optimized,
.div_trunc_optimized,
.div_floor_optimized,
.div_ceil_optimized,
.div_exact_optimized,
.rem_optimized,
.mod_optimized,
Expand Down Expand Up @@ -1710,6 +1719,8 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
.div_trunc_optimized,
.div_floor,
.div_floor_optimized,
.div_ceil,
.div_ceil_optimized,
.div_exact,
.div_exact_optimized,
.rem,
Expand Down
2 changes: 2 additions & 0 deletions src/Air/types_resolved.zig
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
.div_trunc_optimized,
.div_floor,
.div_floor_optimized,
.div_ceil,
.div_ceil_optimized,
.div_exact,
.div_exact_optimized,
.rem,
Expand Down
4 changes: 4 additions & 0 deletions src/Liveness.zig
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ pub fn categorizeOperand(
.div_trunc,
.div_floor,
.div_exact,
.div_ceil,
.rem,
.mod,
.bit_and,
Expand Down Expand Up @@ -274,6 +275,7 @@ pub fn categorizeOperand(
.div_trunc_optimized,
.div_floor_optimized,
.div_exact_optimized,
.div_ceil_optimized,
.rem_optimized,
.mod_optimized,
.neg_optimized,
Expand Down Expand Up @@ -896,6 +898,8 @@ fn analyzeInst(
.div_floor_optimized,
.div_exact,
.div_exact_optimized,
.div_ceil,
.div_ceil_optimized,
.rem,
.rem_optimized,
.mod,
Expand Down
2 changes: 2 additions & 0 deletions src/Liveness/Verify.zig
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
.div_floor_optimized,
.div_exact,
.div_exact_optimized,
.div_ceil,
.div_ceil_optimized,
.rem,
.rem_optimized,
.mod,
Expand Down
Loading
Loading