---
title: "@subWithOverflow em Zig — Referência e Exemplos"
url: "https://ziglang.com.br/builtins/@subwithoverflow-em-zig-refer%C3%AAncia-e-exemplos/"
markdown_url: "https://ziglang.com.br/builtins/@subwithoverflow-em-zig-refer%C3%AAncia-e-exemplos.MD"
description: "Referência completa do @subWithOverflow em Zig. Subtração com detecção de overflow retornando resultado e flag. Exemplos práticos pt-BR."
date: "2026-02-21"
author: "Zig Brasil"
---

# @subWithOverflow em Zig — Referência e Exemplos

Referência completa do @subWithOverflow em Zig. Subtração com detecção de overflow retornando resultado e flag. Exemplos práticos pt-BR.


# @subWithOverflow em Zig

O `@subWithOverflow` realiza uma subtração e indica se houve **overflow** (ou underflow). Retorna uma tupla com o resultado (possivelmente truncado) e um bit de overflow. Especialmente útil com tipos unsigned, onde subtrair um valor maior causa underflow.

## Sintaxe

```zig
@subWithOverflow(a: T, b: T) struct { T, u1 }
```

## Parâmetros

- **a** (`T`): Minuendo (tipo inteiro).
- **b** (`T`): Subtraendo (mesmo tipo inteiro).

## Valor de retorno

Retorna uma tupla `struct { T, u1 }`:
- **[0]** (`T`): Resultado da subtração (truncado se overflow).
- **[1]** (`u1`): `1` se houve overflow/underflow, `0` caso contrário.

## Exemplos práticos

### Exemplo 1: Detecção de underflow em unsigned

```zig
const std = @import("std");

pub fn main() void {
    // Sem underflow
    const r1 = @subWithOverflow(@as(u8, 100), @as(u8, 50));
    std.debug.print("100 - 50 = {} (overflow: {})\n", .{ r1[0], r1[1] });
    // 100 - 50 = 50 (overflow: 0)

    // Com underflow: 50 - 100 em u8
    const r2 = @subWithOverflow(@as(u8, 50), @as(u8, 100));
    std.debug.print("50 - 100 = {} (overflow: {})\n", .{ r2[0], r2[1] });
    // 50 - 100 = 206 (overflow: 1)  — wraps around
}
```

### Exemplo 2: Subtração segura com fallback

```zig
const std = @import("std");

fn subSegura(a: u32, b: u32) u32 {
    const resultado = @subWithOverflow(a, b);
    if (resultado[1] != 0) {
        return 0; // Saturar em zero em vez de overflow
    }
    return resultado[0];
}

pub fn main() void {
    std.debug.print("{}\n", .{subSegura(100, 30)});  // 70
    std.debug.print("{}\n", .{subSegura(10, 50)});    // 0 (saturado)
}
```

### Exemplo 3: Cálculo de diferença absoluta

```zig
const std = @import("std");

fn diferencaAbsoluta(a: u32, b: u32) u32 {
    const r = @subWithOverflow(a, b);
    if (r[1] != 0) {
        // a < b, calcular b - a
        return b - a;
    }
    return r[0];
}

pub fn main() void {
    std.debug.print("|100 - 30| = {}\n", .{diferencaAbsoluta(100, 30)}); // 70
    std.debug.print("|30 - 100| = {}\n", .{diferencaAbsoluta(30, 100)}); // 70
}
```

## Casos de uso comuns

1. **Aritmética segura**: Evitar panic por underflow em tipos unsigned.
2. **Saturação**: Implementar subtração saturante (clampar em zero).
3. **Diferença absoluta**: Calcular distância entre valores sem saber a ordem.
4. **Timers e contadores**: Detectar wrap-around em contadores que decrementam.

## Underflow vs. Overflow: a diferença para tipos unsigned

Para tipos **unsigned**, subtrair um valor maior causa **underflow** — o resultado "dá a volta" pelo zero para o valor máximo do tipo. Por exemplo, `0u8 - 1` resulta em `255`. O flag de overflow retornado por `@subWithOverflow` detecta exatamente esse caso.

Para tipos **signed**, overflow ocorre quando o resultado sai do intervalo representável em ambas as direções: `i8_min - 1` causa overflow positivo e `i8_max + 1` causa overflow negativo.

## Considerações de desempenho

Como `@mulWithOverflow` e `@addWithOverflow`, `@subWithOverflow` compila para uma única instrução de subtração seguida de verificação do carry flag ou overflow flag do processador. Em x86-64, isso é `sub` com verificação de `CF` (unsigned) ou `OF` (signed). O custo é negligível comparado a qualquer verificação manual equivalente.

## Subtração saturante vs. com overflow

Existem dois padrões comuns ao lidar com subtração que pode underflow:

```zig
const std = @import("std");

// 1. Subtração saturante: clampeia em zero
fn subSaturante(a: u32, b: u32) u32 {
    const r = @subWithOverflow(a, b);
    return if (r[1] != 0) 0 else r[0];
}

// 2. Subtração com erro: retorna erro em caso de underflow
fn subComErro(a: u32, b: u32) !u32 {
    const r = @subWithOverflow(a, b);
    if (r[1] != 0) return error.Underflow;
    return r[0];
}

// 3. Diferença absoluta: sempre positivo
fn difAbsoluta(a: u32, b: u32) u32 {
    if (a >= b) return a - b;
    return b - a;
}
```

## Comparação com equivalente em C

Em C, detectar underflow de subtração unsigned requer comparação prévia:

```c
// C: verificação manual (correta mas verbosa)
unsigned int sub_segura(unsigned int a, unsigned int b) {
    if (b > a) return 0; // underflow: saturar em zero
    return a - b;
}

// C alternativa: usando wrapping e comparação posterior (menos intuitivo)
unsigned int resultado = a - b;
if (resultado > a) { /* underflow ocorreu */ }
```

Em Zig, `@subWithOverflow` é direto e não requer comparação prévia:

```zig
fn subSegura(a: u32, b: u32) u32 {
    const r = @subWithOverflow(a, b);
    return if (r[1] != 0) 0 else r[0];
}
```

## Erros comuns

**1. Usar subtração direta com tipos unsigned quando underflow é possível:**
```zig
// PERIGOSO em Debug: panic se b > a
// UNDEFINED BEHAVIOR em ReleaseFast
const resultado = a - b;

// CORRETO: usar @subWithOverflow para tratar o caso
const r = @subWithOverflow(a, b);
if (r[1] != 0) return error.Underflow;
return r[0];
```

**2. Confundir overflow e underflow:** Para unsigned, subtrair mais do que o valor disponível é underflow (não overflow, tecnicamente). O Zig usa o termo "overflow" para ambos, pois a flag do hardware não distingue — ela indica apenas que o resultado não cabe no tipo.

## Perguntas Frequentes

**P: Para tipos signed, `@subWithOverflow` detecta overflow nos dois sentidos?**

R: Sim. Para `i8`, tanto `127 - (-1)` (resultado 128, acima de i8_max) quanto `-128 - 1` (resultado -129, abaixo de i8_min) retornam flag de overflow igual a 1.

**P: É possível usar `@subWithOverflow` para implementar contadores que decrementam com wrap-around?**

R: Sim — e o resultado truncado em `[0]` é exatamente o valor com wrap. Você pode verificar com o flag quando o contador "deu a volta" e resetar estados conforme necessário.

**P: `@subWithOverflow` funciona com `usize` para aritmética de ponteiros?**

R: Sim. `usize` é tratado como qualquer outro tipo unsigned. Mas para aritmética de ponteiros, é mais idiomático usar as funções de `std.mem` ou aritmética de ponteiros direta com verificações de bounds.

## Builtins relacionados

- [@addWithOverflow](/builtins/add-with-overflow/) — Adição com detecção de overflow
- [@mulWithOverflow](/builtins/mul-with-overflow/) — Multiplicação com detecção de overflow
- [@min](/builtins/min/) / [@max](/builtins/max/) — Limitar valores
- [@shlExact](/builtins/shl-exact/) — Shift left exato

## Tutoriais relacionados

- [Aritmética em Zig](/tutoriais/aritmetica-zig/)
- [Sistema de Tipos do Zig](/tutoriais/tipos/)
- [Introdução ao Zig](/tutoriais/introducao-ao-zig/)
