HC(1)

NAME
hc - an arbitrary precision fixed-point number calculator

SYNOPSIS

hc [ expr ... ]

DESCRIPTION
hc is a simple desk calculator for integer and fixed-point calculations. It has some similarities with e.g. bc(1) and dc(1).

When hc is invoked with arguments, all arguments are concatenated, parsed and evaluated as a single expression, the program will then print the result before exiting. The behavior is similar if input is provided via stdin. If input is provided via both arguments and stdin, the arguments are prioritized. In the absence of any arguments or input via stdin, hc will enter an interactive mode with a read-eval-print-loop.

Numbers in hc are by default rational numbers with infinite precision. They can be thought of as binary two's complement fixed-point numbers with infinite number of integer and fractional bits. The number of integer and fractional bits can also be set arbitrarily and independently.

Numbers can be entered in either decimal, binary, octal or hexadecimal numerals. The result is also shown in multiple bases.

hc is meant to be used as a desk calculator and may be useful for e.g.

•   converting numbers between different bases,

•   performing operations on two's complement numbers with any bit width or precision,

•   performing rational number calculations, with both input and output having mixed bases,

•   converting between fractions and rational numbers with repeating digits.

hc differs from bc(1) in several ways:

•   input base is selected with prefix, no need to set ibase, expressions can therefore contain multiple values in different bases,

•   output is always printed in multiple bases, no need to set obase,

•   negative values are always shown as two's complement numbers in binary, octal and hexadecimal,

•   length and scale is specified in number of binary digits instead of decimal digits,

•   length and scale can be infinite, infinitively repeating digits can be used in input and are shown in output,

•   fewer bases are supported: only binary, octal, decimal and hexadecimal,

•   no variables, user functions, strings or control flow.

  Types

All values are numbers in the form of binary two's complement numbers, each with a signedness, integer width and fractional width. The signedness can be either unspecified (default), unsigned or signed. Each width is either unspecified (default) or a fixed non-negative integer. Numbers with an unspecified will normally act as a signed number, but if used in an operation with a signed or unsigned number it may inherit the other operand's signedness. Numbers with an unspecified width can be arbitrarily large, but may inherit a limited width and may be truncated if used in an operation together with an operand that has a fixed integer width. Similarly, numbers with an unspecified width can be arbitarily precise, but will inherit a limited width and may be truncated if used in an operation together with an operand that has a fixed fractional width. The type of a number can be specified by providing a type specifier. A specifier may contain a prefix, an integer width m and a fractional width f. The fractional width must be prepended with a period (.) character. The prefix i or u specifies a signed or unsigned integer, respectively. Similarly, the prefix q or uq specifies a signed or unsigned fixed-point number. The table below lists all possible variants of type specifiers.

Table: Type Specifier Variants
Type specifier | Signedness | Integer width | Fractional width
i | signed | unspecified | 0
im | signed | m | 0
u | unsigned | unspecified | 0
um | unsigned | m | 0
q | signed | unspecified | unspecified
qm | signed | m | unspecified
q.f | signed | unspecified | f
qm.f | signed | m | f
uq | unsigned | unspecified | unspecified
uqm | unsigned | m | unspecified
uq.f | unsigned | unspecified | f
uqm.f | unsigned | m | f
m.f | unspecified | m | f
.f | unspecified | unspecified | f
m | unspecified | m | unspecified

  Literals
All literals are non-negative numbers. Number tokens may have an integer part and a fractional part separated by a radix point represented by the period (.) character. Either the integer part or the fractional part may be omitted but not both. The period is only required if there is a fractional part.

The number may start with a prefix that determines the base of the number. The digits in the integer and fractional part must be valid in the base specified by the prefix. If no prefix is specified, the decimal base is assumed. The below table list the prefix and valid digits for each base. The prefix is case-sensitive while the alphabetic digits are not.

Table: Number Bases
Name | Prefix | Digits
Binary | 0b | 0 1
Octal | 0o | 0 1 2 3 4 5 6 7
Decimal | 0d | 0 1 2 3 4 5 6 7 8 9
Hexadecimal | 0x | 0 1 2 3 4 5 6 7 8 9 a b c d e f

If a number has a prefix that corresponds to the binary, octal or hexadecimal base; the integer part may start with the maximal digit of the base enclosed in parentheses. This will indicate that the digit is infinitively repeating. If the number is signed, this will represent a finite negative number.

The fractional part may contain an infinitively repeating sequence of digits. The sequence must only appear once at the end of the fractional part and be enclosed in parentheses.

Scientific notation can be used for numbers in the decimal base. The number may end with the character e or E followed by a positive or negative integer. The number will then be multiplied by 10 to the power of the specified integer. The full grammar of a number is listed below.

        number      : decimal
                    | non_decimal

        decimal     : stem
                    | stem e integer
                    | stem E integer

        non_decimal : prefix stem
                    | prefix ( max_digit ) stem
                    ;

        stem        : integer
                    | . fraction
                    | integer .
                    | integer . fraction
                    ;

        integer     : base_digit
                    | integer base_digit
                    ;

        fraction    : base_digit
                    | integer base_digit
                    | integer ( integer )
                    ;

A number literal may be immediately followed by a type specifier in order to specify the type of the number. If the type specifier does not have a prefix, it must be prepended with an apostrophe (') character.

  Expressions
Literals can be used together with operators to form expressions. A single literal is the most basic expression. There are prefix unary and infix binary operators. Expressions can also be grouped by enclosing with parentheses. For symmetric binary operations of two operands with differing types, a max_type between the two operands' types is determined. Before the operation is performed, both of the operands are casted to this type. The three properties of the new type, signedness, integer width, and fractional width, are all determined independently according to the following rules:

•   If a property is set to unspecified for both types, the resulting property will be unspecified.

•   If a property is set for one operand and unspecified for the other, the resulting type will have inherit the set property.

•   If both operands have a set property and they differ,

    •   for signedness, the resulting type will be signed,

    •   for a width, the resulting width will be the maximum of the two operands' widths.

Below is a table of all valid expressions in order of decreasing precedence. Operators not separated by horizontal lines have the same precedence and are grouped according to their associativity.

Table: Expressions in Order of Decreasing Precedence
Syntax | Name | Type of Result | Associativity
( expr ) | Grouping | type of expr | N/A
expr ' type | Casting | type | N/A
- expr | Negation | type of expr | N/A
~ expr | Bitwise Not |  |
lexpr * rexpr | Multiplication | max_type(lexpr, rexpr) | Left
lexpr / rexpr | Division |  |
lexpr % rexpr | Remainder |  |
lexpr + rexpr | Addition | max_type(lexpr, rexpr) | Left
lexpr - rexpr | Subtraction |  |
lexpr << rexpr | Left shift | type of lexpr | Left
lexpr >> rexpr | Right shift |  |
lexpr & rexpr | Bitwise And | max_type(lexpr, rexpr) | Left
lexpr ^ rexpr | Bitwise Xor | max_type(lexpr, rexpr) | Left
lexpr | rexpr | Bitwise Or | max_type(lexpr, rexpr) | Left

FILES

  History
If hc is compiled with the readline feature, hc will read and write to a history file to maintain history between sessions. The location of the history file is dependent on platform:
Platform | Location | Example
Linux | $XDG_CACHE_HOME/hc/history or $HOME/.cache/hc/history | /home/noah/.cache/hc/history
macOS | $HOME/Library/Caches/hc/history | /Users/noah/Library/Caches/hc/history
Windows | ${FOLDERID_LocalAppData}/hc/history | C:\Users\noah\AppData\Local\hc\history

EXIT STATUS

On a fatal error, the exit status is 1. Otherwise the exit status will depend on the last evaluated expression. The exit status will be 1 if an error was encountered during parsing or evaluation, otherwise 0.

EXAMPLES
hc mostly works like a typical calculator, one can enter expressions that will be evaluated:

    > (2 + 7) * 0xd
    117
     = 0b111_0101
     = 0o165
     = 0x75

Numbers can be given a specific width with a type specifier:

    > 77u8
    77
     = 0b0100_1101
     = 0o115
     = 0x4d

For negative numbers, the non-decimal representations will display the two's complement value:

    > -77i8
    -77 (= 179)
     = 0b1011_0011
     = 0o263
     = 0xb3

The signedness of a number affects how it is extended when operands differ in width:

    > 32u8 + (-1)'i4
    31
     = 0b0001_1111
     = 0o037
     = 0x1f
    > 32u8 + (-1)'u4
    47
     = 0b0010_1111
     = 0o057
     = 0x2fa

Values may not only have integer values, they may also contain a fractional part:

    > 10/4
    2.5 (= 5/2)
     = 0b10.1
     = 0o2.4
     = 0x2.8

By default, the precision is infinite, if a value cannot be represented by a finite number of digits, infinitively repeating sequences of digits will be enclosed with parentheses:

    > 1/3
    0.(3) (= 1/3)
     = 0b0.(01)
     = 0o0.(25)
     = 0x0.(5)

If a fractional width is specified, the fractional part will be truncated:

    > 1/3q.8
    0.332_031_25 (= 85/256)
     = 0b0.0101_0101
     = 0o0.252
     = 0x0.55

Negative numbers with an unspecified integer width will have an infinitively repeating digit in the integer part for non-decimal bases:

    > -5.25
    -5.25 (= -21/4)
     = 0b(1)010.11
     = 0o(7)2.6
     = 0x(f)a.c

VERSION
This manual was written for hc 0.1.1.

SEE ALSO
bc(1), dc(1),

AUTHORS
Noah Hellman <noah@hllmn.net>