diff --git a/src/codegen/c.zig b/src/codegen/c.zig
index b814be44453c..0ae4c522ccf0 100644
--- a/src/codegen/c.zig
+++ b/src/codegen/c.zig
@@ -1663,10 +1663,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
             .mul           => try airBinOp (f, inst, " * "),
             // TODO use a different strategy for div that communicates to the optimizer
             // that wrapping is UB.
-            .div_float, .div_exact, .div_trunc => try airBinOp( f, inst, " / "),
-            .div_floor                         => try airBinOp( f, inst, " divfloor "),
+            .div_float, .div_exact => try airBinOp( f, inst, " / "),
+            .div_trunc     => try airBinOpBuiltinCall(f, inst, "div_trunc"),
+            .div_floor     => try airBinOpBuiltinCall(f, inst, "div_floor"),
             .rem           => try airBinOp( f, inst, " % "),
-            .mod           => try airBinOp( f, inst, " mod "), // TODO implement modulus division
+            .mod           => try airBinOpBuiltinCall(f, inst, "mod"),
 
             .addwrap => try airWrapOp(f, inst, " + ", "addw_"),
             .subwrap => try airWrapOp(f, inst, " - ", "subw_"),
@@ -3463,6 +3464,41 @@ fn airBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !C
     return local;
 }
 
+fn airBinOpBuiltinCall(f: *Function, inst: Air.Inst.Index, fn_name: [*:0]const u8) !CValue {
+    if (f.liveness.isUnused(inst)) return CValue.none;
+
+    const inst_ty = f.air.typeOfIndex(inst);
+    const local = try f.allocLocal(inst_ty, .Const);
+    const bin_op = f.air.instructions.items(.data)[inst].bin_op;
+    const lhs_ty = f.air.typeOf(bin_op.lhs);
+    const target = f.object.dg.module.getTarget();
+    const writer = f.object.writer();
+
+    // For binary operations @TypeOf(lhs)==@TypeOf(rhs), so we only check one.
+    if (lhs_ty.isInt()) {
+        const int_info = lhs_ty.intInfo(target);
+        const c_bits = toCIntBits(int_info.bits) orelse
+            return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{});
+        const prefix_byte: u8 = switch (int_info.signedness) {
+            .signed => 'i',
+            .unsigned => 'u',
+        };
+        try writer.print(" = zig_{s}_{c}{d}", .{ fn_name, prefix_byte, c_bits });
+    } else if (lhs_ty.isRuntimeFloat()) {
+        const c_bits = lhs_ty.floatBits(target);
+        try writer.print(" = zig_{s}_f{d}", .{ fn_name, c_bits });
+    } else {
+        return f.fail("TODO: C backend: implement airBinOpBuiltinCall for type {s}", .{@tagName(lhs_ty.tag())});
+    }
+
+    try writer.writeByte('(');
+    try f.writeCValue(writer, try f.resolveInst(bin_op.lhs));
+    try writer.writeAll(", ");
+    try f.writeCValue(writer, try f.resolveInst(bin_op.rhs));
+    try writer.writeAll(");\n");
+    return local;
+}
+
 fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue {
     const ty_pl = f.air.instructions.items(.data)[inst].ty_pl;
     const extra = f.air.extraData(Air.Cmpxchg, ty_pl.payload).data;
diff --git a/src/link/C/zig.h b/src/link/C/zig.h
index 7259ba19cee8..130e6dc7f6ff 100644
--- a/src/link/C/zig.h
+++ b/src/link/C/zig.h
@@ -750,3 +750,97 @@ static inline uint128_t zig_bit_reverse_u128(uint128_t value, uint8_t zig_type_b
 }
 
 #define zig_bit_reverse_i128 zig_bit_reverse_u128
+
+static inline float zig_div_truncf(float numerator, float denominator) {
+    return __builtin_truncf(numerator / denominator);
+}
+
+static inline double zig_div_trunc(double numerator, double denominator) {
+    return __builtin_trunc(numerator / denominator);
+}
+
+static inline long double zig_div_truncl(long double numerator, long double denominator) {
+    return __builtin_truncf(numerator / denominator);
+}
+
+#define zig_div_trunc_f16  zig_div_truncf
+#define zig_div_trunc_f32  zig_div_truncf
+#define zig_div_trunc_f64  zig_div_trunc
+#define zig_div_trunc_f80  zig_div_truncl
+#define zig_div_trunc_f128 zig_div_truncl
+
+#define zig_div_trunc_int(ZigType, CType) \
+    static inline CType zig_div_trunc_##ZigType(CType numerator, CType denominator) { \
+        return numerator / denominator; \
+    }
+
+zig_div_trunc_int(  u8,   uint8_t)
+zig_div_trunc_int(  i8,    int8_t)
+zig_div_trunc_int( u16,  uint16_t)
+zig_div_trunc_int( i16,   int16_t)
+zig_div_trunc_int( u32,  uint32_t)
+zig_div_trunc_int( i32,   int32_t)
+zig_div_trunc_int( u64,  uint64_t)
+zig_div_trunc_int( i64,   int64_t)
+zig_div_trunc_int(u128, uint128_t)
+zig_div_trunc_int(i128,  int128_t)
+
+#define zig_div_floorf(numerator, denominator) \
+    __builtin_floorf((float)(numerator) / (float)(denominator))
+
+#define zig_div_floor(numerator, denominator) \
+    __builtin_floor((double)(numerator) / (double)(denominator))
+
+#define zig_div_floorl(numerator, denominator) \
+    __builtin_floorl((long double)(numerator) / (long double)(denominator))
+
+#define zig_div_floor_f16  zig_div_floorf
+#define zig_div_floor_f32  zig_div_floorf
+#define zig_div_floor_f64  zig_div_floor
+#define zig_div_floor_f80  zig_div_floorl
+#define zig_div_floor_f128 zig_div_floorl
+
+#define zig_div_floor_u8   zig_div_floorf
+#define zig_div_floor_i8   zig_div_floorf
+#define zig_div_floor_u16  zig_div_floorf
+#define zig_div_floor_i16  zig_div_floorf
+#define zig_div_floor_u32  zig_div_floor
+#define zig_div_floor_i32  zig_div_floor
+#define zig_div_floor_u64  zig_div_floor
+#define zig_div_floor_i64  zig_div_floor
+#define zig_div_floor_u128 zig_div_floorl
+#define zig_div_floor_i128 zig_div_floorl
+
+static inline float zig_modf(float numerator, float denominator) {
+    return (numerator - (zig_div_floorf(numerator, denominator) * denominator));
+}
+
+static inline double zig_mod(double numerator, double denominator) {
+    return (numerator - (zig_div_floor(numerator, denominator) * denominator));
+}
+
+static inline long double zig_modl(long double numerator, long double denominator) {
+    return (numerator - (zig_div_floorl(numerator, denominator) * denominator));
+}
+
+#define zig_mod_f16  zig_modf
+#define zig_mod_f32  zig_modf
+#define zig_mod_f64  zig_mod
+#define zig_mod_f80  zig_modl
+#define zig_mod_f128 zig_modl
+
+#define zig_mod_int(ZigType, CType) \
+    static inline CType zig_mod_##ZigType(CType numerator, CType denominator) { \
+        return (numerator - (zig_div_floor_##ZigType(numerator, denominator) * denominator)); \
+    }
+
+zig_mod_int(  u8,   uint8_t)
+zig_mod_int(  i8,    int8_t)
+zig_mod_int( u16,  uint16_t)
+zig_mod_int( i16,   int16_t)
+zig_mod_int( u32,  uint32_t)
+zig_mod_int( i32,   int32_t)
+zig_mod_int( u64,  uint64_t)
+zig_mod_int( i64,   int64_t)
+zig_mod_int(u128, uint128_t)
+zig_mod_int(i128,  int128_t)
diff --git a/test/behavior/int_div.zig b/test/behavior/int_div.zig
index 8ab8ad9e42c3..0c50ce89096b 100644
--- a/test/behavior/int_div.zig
+++ b/test/behavior/int_div.zig
@@ -4,7 +4,6 @@ const expect = std.testing.expect;
 
 test "integer division" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
     if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
 
diff --git a/test/behavior/math.zig b/test/behavior/math.zig
index 0d10113fb04c..67f7fd101de6 100644
--- a/test/behavior/math.zig
+++ b/test/behavior/math.zig
@@ -385,7 +385,6 @@ fn testBinaryNot(x: u16) !void {
 }
 
 test "division" {
-    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
@@ -394,22 +393,18 @@ test "division" {
     try testDivision();
     comptime try testDivision();
 }
+
 fn testDivision() !void {
     try expect(div(u32, 13, 3) == 4);
-    try expect(div(f16, 1.0, 2.0) == 0.5);
     try expect(div(f32, 1.0, 2.0) == 0.5);
 
     try expect(divExact(u32, 55, 11) == 5);
     try expect(divExact(i32, -55, 11) == -5);
-    try expect(divExact(f16, 55.0, 11.0) == 5.0);
-    try expect(divExact(f16, -55.0, 11.0) == -5.0);
     try expect(divExact(f32, 55.0, 11.0) == 5.0);
     try expect(divExact(f32, -55.0, 11.0) == -5.0);
 
     try expect(divFloor(i32, 5, 3) == 1);
     try expect(divFloor(i32, -5, 3) == -2);
-    try expect(divFloor(f16, 5.0, 3.0) == 1.0);
-    try expect(divFloor(f16, -5.0, 3.0) == -2.0);
     try expect(divFloor(f32, 5.0, 3.0) == 1.0);
     try expect(divFloor(f32, -5.0, 3.0) == -2.0);
     try expect(divFloor(i32, -0x80000000, -2) == 0x40000000);
@@ -424,10 +419,6 @@ fn testDivision() !void {
     try expect(divTrunc(i32, -5, 3) == -1);
     try expect(divTrunc(i32, 9, -10) == 0);
     try expect(divTrunc(i32, -9, 10) == 0);
-    try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
-    try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
-    try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
-    try expect(divTrunc(f16, -9.0, 10.0) == 0.0);
     try expect(divTrunc(f32, 5.0, 3.0) == 1.0);
     try expect(divTrunc(f32, -5.0, 3.0) == -1.0);
     try expect(divTrunc(f32, 9.0, -10.0) == 0.0);
@@ -468,6 +459,32 @@ fn testDivision() !void {
         );
     }
 }
+
+test "division half-precision floats" {
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+    try testDivisionFP16();
+    comptime try testDivisionFP16();
+}
+
+fn testDivisionFP16() !void {
+    try expect(div(f16, 1.0, 2.0) == 0.5);
+
+    try expect(divExact(f16, 55.0, 11.0) == 5.0);
+    try expect(divExact(f16, -55.0, 11.0) == -5.0);
+
+    try expect(divFloor(f16, 5.0, 3.0) == 1.0);
+    try expect(divFloor(f16, -5.0, 3.0) == -2.0);
+    try expect(divTrunc(f16, 5.0, 3.0) == 1.0);
+    try expect(divTrunc(f16, -5.0, 3.0) == -1.0);
+    try expect(divTrunc(f16, 9.0, -10.0) == 0.0);
+    try expect(divTrunc(f16, -9.0, 10.0) == 0.0);
+}
+
 fn div(comptime T: type, a: T, b: T) T {
     return a / b;
 }