---
title: "Decimal Values"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Decimal Values}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>")
```

R's doubles are binary fractions. They're fast and almost always good enough,
but they can't represent most decimal numbers exactly — which is why this
happens:

```{r}
0.1 + 0.2 == 0.3
```

Most of the time you can shrug this off. But if you're adding up invoices,
reconciling accounts, or storing prices, "almost 0.3" doesn't cut it. The
decimal package gives you vectors that hold decimal numbers *exactly* and
compute with them exactly, following the same
[General Decimal Arithmetic](https://speleotrove.com/decimal/decarith.pdf)
standard as Python's `decimal` module.

This vignette shows you how to create decimal vectors and work with them
day-to-day. A companion vignette, `vignette("contexts-and-signals")`, covers
the arithmetic context: precision, rounding modes, and how conditions like
overflow are handled.

```{r setup}
library(decimal)
```

## Creating decimal vectors

The best way to create a decimal is from a string, because a string can say
*exactly* what you mean:

```{r}
x <- decimal(c("1.20", "2.30", "3.40"))
x
```

Integers work too, and special values are written the way the standard spells
them:

```{r}
decimal(c(1L, 2L, NA_integer_))
decimal(c("1.20", "-0", "Infinity", "NaN"))
```

Values are immutable and stored as text internally, so converting back with
`format()` or `as.character()` always reproduces them exactly — nothing is
lost round-tripping through a CSV file or a database column:

```{r}
as.character(x)
```

## Scale: how many decimal places?

Every decimal vector has a single shared **scale**: the number of fractional
digits stored for every element. If you don't specify it, decimal infers the
largest number of fractional digits present, and pads (never rounds!) the
other elements to match:

```{r}
decimal(c("1.2", "1.20", "1.234"))
```

Because scale belongs to the vector rather than to each element,
`decimal("1.2")` and `decimal("1.20")` are the same value:

```{r}
decimal("1.2") == decimal("1.20")
```

## What about doubles?

You might expect `decimal(0.1)` to work. It doesn't, and that's deliberate:
the double `0.1` is not actually 0.1 — its exact binary value needs 55
fractional digits to write out in decimal! So converting a double is explicit,
via `as_decimal()` or `decimal_from_double()`, and you have to say how many
digits you want to keep:

```{r}
decimal_from_double(0.1, scale = 25)
```

If what you want is decimal `0.1`, write `decimal("0.1")`:

```{r}
decimal("0.1")
```

If your workflow consistently uses one scale — say, you always want 7 digits —
set the opt-in default so you don't have to repeat yourself. An explicit
`scale` argument still wins:

```{r}
withr::with_options(
  list(decimal.default_scale = 7L),
  as_decimal(0.002)
)
```

Set it globally with `options(decimal.default_scale = 7L)`, or use
`withr::local_options()` when the default should apply only within a function
or a test.

## Decimals are well-behaved vectors

Decimal vectors are built on [vctrs](https://vctrs.r-lib.org), so they behave
the way you'd hope inside data frames and tibbles, and with sorting, matching,
and friends:

```{r}
tibble::tibble(
  item  = c("coffee", "bagel", "juice"),
  price = decimal(c("2.50", "1.25", "3.95"))
)
```

They combine freely with integers, promoting to the common (largest) scale:

```{r}
vctrs::vec_c(decimal("1.20"), 2L, NA)
```

But they refuse to *implicitly* combine with doubles or character strings:

```{r, error = TRUE}
vctrs::vec_c(decimal("1.20"), 0.5)
```

That's the same design decision as above, applied consistently: a double needs
a scale decided for it, and a string needs to be parsed (which can fail), so
neither is a lossless, always-safe promotion the way integer is. When you mean
it, say it — call `decimal()` or `as_decimal()` explicitly.

## Arithmetic

Arithmetic is vectorized, context-controlled, and keeps track of
significance:

```{r}
decimal("1.20") + decimal("2.3")
sum(decimal(c("1.20", "2.30", "3.40")))
mean(decimal(c("1", "2", "3")))
```

One difference from base R worth knowing about: `%%` and `%/%`. Base R floors
integer division toward negative infinity, while General Decimal Arithmetic
truncates toward zero, so results differ for negative operands:

```{r}
-7 %% 4                        # base R: floored, remainder takes divisor's sign
decimal("-7") %% decimal("4")  # decimal: truncated, remainder takes dividend's sign

-7 %/% 4
decimal("-7") %/% decimal("4")
```

## Special values

decimal supports the full menagerie: `NA`, NaNs, infinities, and signed zero —
and it keeps R's missing value distinct from decimal's not-a-number:

```{r}
x <- decimal(c(NA_character_, "NaN", "sNaN", "Infinity", "-0"))
x
is.na(x)
is.nan(x)
number_class(x)
```

A few things to note:

* `NA` is an R missing value — it's absent from the computation entirely.

* `NaN` (quiet NaN) and `sNaN` (signaling NaN) are decimal not-a-number values
  that participate in arithmetic. A quiet NaN propagates silently; an sNaN
  raises the `invalid_operation` signal, which is an error under the default
  context — see `vignette("contexts-and-signals")`.

* `is.na()` returns `TRUE` for both `NA` and decimal NaNs, matching base R's
  own `is.na(NaN)`. Use `is.nan()` to tell a decimal NaN apart from a missing
  value.

* Signed zero survives formatting: `-0` prints as `-0`, not `0`.

## Rounding and other decimal tools

`quantize()` is the workhorse for rounding: it rescales `x` to the scale
declared by `quantum`, using the active context's rounding mode. It's the
operation behind `round()` and `signif()`, and it reads naturally for the most
common case — rounding to cents:

```{r}
quantize(decimal("1.2345"), decimal("0.01"))
```

`normalize()` strips shared trailing zeros down to the finest scale the vector
actually needs, without discarding any element's significance:

```{r}
normalize(decimal(c("1.2300", "1.2")))
```

`fma(a, b, c)` computes `a * b + c` as one fused operation with a single
rounding at the end, instead of rounding after the multiplication and again
after the addition:

```{r}
fma(decimal("2"), decimal("3"), decimal("4"))
```

`same_quantum()` reports whether two vectors share the same declared scale
(remember, scale is a per-vector property):

```{r}
same_quantum(decimal(c("1.20", "2.0")), decimal(c("2.30", "3.00")))
```

And `adjusted()` returns each value's adjusted exponent — the exponent it
would have in scientific notation with a single digit before the point:

```{r}
adjusted(decimal(c("1.2300", "1E-3")))
```

## Where to next

Everything above used the default arithmetic settings: 28 digits of precision,
round-half-even, and errors on division by zero, invalid operations, and
overflow. All of that is configurable through the decimal *context* — read on
in `vignette("contexts-and-signals")`.
