.Dd $Mdocdate: November 7 2022 $ .Dt HC 1 .Sh NAME .Nm hc .Nd an arbitrary bit-width fixed-point number calculator .Sh SYNOPSIS .Nm .Op Ar program ... .Sh DESCRIPTION .Nm is a simple desk calculator for integer and fixed-point two's complement calculations. It has some similarities with e.g. .Xr bc 1 and .Xr dc 1 . .Pp When .Nm is invoked with arguments, all arguments are concatenated, parsed and evaluated as a program. .Nm will then print the result before exiting. The behavior is similar if input is provided via standard input. If input is provided via both arguments and standard input, the arguments are prioritized. In the absence of input, .Nm will enter an interactive mode with a REPL, i.e. a read-eval-print-loop. .Pp Numbers in .Nm are by default rational numbers with infinite precision. They can be thought of as binary two's complement fixed-point numbers with either infinite or a specified number of integer and fractional bits. .Pp Numbers can be entered in either decimal, binary, octal or hexadecimal numerals. The result is also shown in multiple bases. .Pp .Nm is meant to be used as a desk calculator and may be useful for e.g. .Bl -bullet .It converting numbers between different bases, .It performing operations on two's complement numbers with any bit width or precision, .It performing rational number calculations, with both input and output having mixed bases, .It converting between fractions and rational numbers with repeating digits. .El .Pp .Nm differs from .Xr bc 1 in several ways: .Bl -bullet .It input base is selected with prefix, no need to set ibase, expressions can therefore contain multiple values in different bases, .It output is always printed in multiple bases, no need to set obase, .It negative values are always shown as two's complement numbers in binary, octal and hexadecimal, .It length and scale is specified in number of binary digits instead of decimal digits, .It length and scale can be infinite, infinitively repeating digits can be used in input and are shown in output, .It fewer bases are supported: only binary, octal, decimal and hexadecimal, .It no user functions, strings or control flow. .El .Ss Types .Pp All values are numbers in the form of binary two's complement fixed-point numbers, each with a .Sy signedness , .Sy integer width and .Sy fractional width . The signedness can be either .Vt unspecified (default), .Vt unsigned or .Vt signed . Each width is either .Vt unspecified (default) or a non-negative integer. Numbers with an unspecified signedness 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 integer 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 fractional 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 .Sy type specifier . A specifier may contain a .Sy prefix , an integer width .Va m and a fractional width .Va f . The fractional width must be prepended with a .Aq period character. The prefix .Vt i or .Vt u specifies a signed or unsigned integer, respectively. Similarly, the prefix .Vt q or .Vt uq specifies a signed or unsigned fixed-point number. The table below lists all possible variants of type specifiers. .Bl -column -offset center "SpecifierXX" "SignednessXX" "Integer widthXX" "Fractional WidthXX" .It Sy Specifier Ta Sy Signedness Ta Sy Integer width Ta Sy Fractional width .It i Ta Vt signed Ta unspecified Ta 0 .It i\fIm\fR Ta Vt signed Ta \fIm\fR Ta 0 .It \fRu Ta Vt unsigned Ta unspecified Ta 0 .It u\fIm\fR Ta Vt unsigned Ta \fIm\fR Ta 0 .It q Ta Vt signed Ta unspecified Ta unspecified .It q\fIm\fR Ta Vt signed Ta \fIm\fR Ta unspecified .It q.\fIf\fR Ta Vt signed Ta unspecified Ta \fIf\fR .It q\fIm\fR.\fIf\fR Ta Vt signed Ta \fIm\fR Ta \fIf\fR .It uq Ta Vt unsigned Ta unspecified Ta unspecified .It uq\fIm\fR Ta Vt unsigned Ta \fIm\fR Ta unspecified .It uq.\fIf\fR Ta Vt unsigned Ta unspecified Ta \fIf\fR .It uq\fIm\fR.\fIf\fR Ta Vt unsigned Ta \fIm\fR Ta \fIf\fR .It \fIm\fR.\fIf\fR Ta unspecified Ta \fIm\fR Ta \fIf\fR .It \.\fIf\fR Ta unspecified Ta unspecified Ta \fIf\fR .It \fIm\fR Ta unspecified Ta \fIm\fR Ta unspecified .El .Ss Literals .Pp All literals are non-negative numbers. Number tokens may have an .Sy integer part and a .Sy fractional part separated by a radix point represented by the .Aq 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. .Pp The number may start with a .Sy prefix that determines the .Sy 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. .Bl -column "HexadecimalXX" "PrefixXX" "" -offset center .It Sy Name Ta Sy Prefix Ta Sy Digits .It Binary Ta Em 0b Ta 0 1 .It Octal Ta Em 0o Ta 0 1 2 3 4 5 6 7 .It Decimal Ta Em 0d Ta 0 1 2 3 4 5 6 7 8 9 .It Hexadecimal Ta Em 0x Ta 0 1 2 3 4 5 6 7 8 9 a b c d e f .El .Pp 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. .Pp 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. .Pp .Sy Scientific notation can be used for numbers in the decimal base. The number may end with the character .Aq e or .Aq E followed by an integer exponent. The number will then be multiplied by 10 to the power of the specified exponent. .Pp The full grammar of a number is listed below, .Em prefix refers to any of the prefixes in the above table, .Em base_digit refers to any of the digits that are valid for the specified prefix, .Em base_digit_max refers to the largest valid digit for the specified prefix. .Bd -literal -offset indent \fBnumber\fR : \fBdecimal\fR | \fBnon_decimal\fR ; \fBdecimal\fR : \fBstem\fR | \fBstem\fR e \fBexponent\fR | \fBstem\fR E \fBexponent\fR ; \fBexponent\fR : \fBinteger\fR | - \fBinteger\fR ; \fBnon_decimal\fR : \fIprefix\fR \fBstem\fR | \fIprefix\fR ( \fIbase_digit_max\fR ) \fBstem\fR ; \fBstem\fR : \fBinteger\fR | . \fBfraction\fR | \fBinteger\fR . | \fBinteger\fR . \fBfraction\fR ; \fBinteger\fR : \fIbase_digit\fR | \fBinteger\fR \fIbase_digit\fR ; \fBfraction\fR : \fIbase_digit\fR | \fBinteger\fR \fIbase_digit\fR | \fBinteger\fR ( \fBinteger\fR ) ; .Ed .Pp 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 .Aq apostrophe character. .Ss Identifiers and Variables Variables can be used to store numeric values. Each variable is associated with an .Sy identifier . An identifer consists of alphanumeric characters and underscores but may not start with a digit. An identifer may also not be identical to any type. Variables may be assigned the value of an expression with the .Aq = operator. The value of a variable may thereafter be referenced by its identifier. The .Va _ variable has special behavior, similar to the .Va Ans variable on a traditional calculator. Every time an outer expression is evaluated, the resulting value will be stored in this variable. Evaluations of intermediary inner expressions within an outer expression do not cause the variable to be overwritten, i.e. the variable will not change during the evaulation of a recursive expression. .Ss Atoms An atom is either a number literal or an identifier. .Ss Program An .Nm program is a sequence of expressions separated by semicolons. Semicolons are not required to terminate an expression, it is only required to separate multiple expressions. Each expression in the program will be evaluated from left to right, and the result of each expression will be displayed in that order. Each time an expression is evaluated, the resulting value will automatically be stored in the .Va _ variable. When .Nm is in interactive mode, the input prompt accepts a program with either a single or multiple expressions. .Ss Expressions Atoms can be used together with operators to form expressions. A single atom 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 .Sy 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: .Bl -bullet .It If a property is set to unspecified for both types, the resulting property will be unspecified. .It If a property is set for one operand and unspecified for the other, the resulting type will have inherit the set property. .It If both operands have a set property and they differ, .Bl -bullet .It for signedness, the resulting type will be signed, .It for a width, the resulting width will be the maximum of the two operands' widths. .El .El .Pp 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. .TS box tab(@) center; l1 | l1 | l1 | l1. Syntax @ Name @ Type of Result @ Associativity _ \fR( \fIexpr\fP ) @ Grouping @ type of \fIexpr\fR @ N/A _ \fIexpr\fP ' \fItype\fP @ Casting @ \fItype\fR @ N/A _ \- \fIexpr\fP @ Negation @ type of \fIexpr\fR @ N/A ~ \fIexpr\fP @ Bitwise Not @ @ _ \fIlexpr\fP ** \fIrexpr\fP @ Exponentiation @ type of \fIlexpr\fR @ Left _ \fIlexpr\fP * \fIrexpr\fP @ Multiplication @ \fBmax_type\fR(\fIlexpr\fR, \fIrexpr\fR) @ Left \fIlexpr\fP / \fIrexpr\fP @ Division @ @ \fIlexpr\fP % \fIrexpr\fP @ Remainder @ @ _ \fIlexpr\fP + \fIrexpr\fP @ Addition @ \fBmax_type\fR(\fIlexpr\fR, \fIrexpr\fR) @ Left \fIlexpr\fP \- \fIrexpr\fP @ Subtraction @ @ _ \fIlexpr\fP << \fIrexpr\fP @ Left shift @ type of \fIlexpr\fR @ Left \fIlexpr\fP >> \fIrexpr\fP @ Right shift @ @ _ \fIlexpr\fP & \fIrexpr\fP @ Bitwise And @ \fBmax_type\fR(\fIlexpr\fR, \fIrexpr\fR) @ Left _ \fIlexpr\fP ^ \fIrexpr\fP @ Bitwise Xor @ \fBmax_type\fR(\fIlexpr\fR, \fIrexpr\fR) @ Left _ \fIlexpr\fP | \fIrexpr\fP @ Bitwise Or @ \fBmax_type\fR(\fIlexpr\fR, \fIrexpr\fR) @ Left _ \fIident\fP = \fIexpr\fP @ Assignment @ type of \fIexpr\fR @ Left .TE .Sh FILES .Ss History .Pp If .Nm is compiled with the .Em readline feature, .Nm will read and write to a history file to maintain history between sessions. The history file will be named .Pa history and will be placed in a cache directory, whose location is dependent on platform: .Bl -column "PlatformX" "Location can be veeeeeeeeeeeeeery long" "" .It Sy Platform Ta Sy Location Ta Sy Example .It Linux Ta Pa $XDG_CACHE_HOME/hc No or Pa $HOME/.cache/hc Ta Pa /home/noah/.cache/hc .It macOS Ta Pa $HOME/Library/Caches/hc Ta Pa /Users/noah/Library/Caches/hc .It Windows Ta Pa ${FOLDERID_LocalAppData}/hc Ta Pa C:\eUsers\enoah\eAppData\eLocal\ehc .El .Sh EXIT STATUS .Pp On any IO error, or if an error is encountered during parsing or evaluation, the exit status will be .Em 1 otherwise .Em 0 . .Sh EXAMPLES .Nm mostly works like a typical calculator, one can enter expressions that will be evaluated: .Bd -literal -offset indent > (2 + 7) * 0xd 117 = 0b111_0101 = 0o165 = 0x75 .Ed .Pp Numbers can be given a specific width with a type specifier: .Bd -literal -offset indent > 77u8 77 = 0b0100_1101 = 0o115 = 0x4d .Ed .Pp For negative numbers, the non-decimal representations will display the two's complement value: .Bd -literal -offset indent > -77i8 -77 (= 179) = 0b1011_0011 = 0o263 = 0xb3 .Ed .Pp The signedness of a number affects how it is extended when operands differ in width: .Bd -literal -offset indent > 32u8 + (-1)'i4 31 = 0b0001_1111 = 0o037 = 0x1f > 32u8 + (-1)'u4 47 = 0b0010_1111 = 0o057 = 0x2fa .Ed .Pp Values may not only have integer values, they may also contain a fractional part: .Bd -literal -offset indent > 10/4 2.5 (= 5/2) = 0b10.1 = 0o2.4 = 0x2.8 .Ed .Pp 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: .Bd -literal -offset indent > 1/3 0.(3) (= 1/3) = 0b0.(01) = 0o0.(25) = 0x0.(5) .Ed .Pp If a fractional width is specified, the fractional part will be truncated: .Bd -literal -offset indent > 1/3q.8 0.332_031_25 (= 85/256) = 0b0.0101_0101 = 0o0.252 = 0x0.55 .Ed .Pp Negative numbers with an unspecified integer width will have an infinitively repeating digit in the integer part for non-decimal bases: .Bd -literal -offset indent > -5.25 -5.25 (= -21/4) = 0b(1)010.11 = 0o(7)2.6 = 0x(f)a.c .Ed .Pp Values assigned to variables may be used in later expressions: .Bd -literal -offset indent > r = 2.5; pi = 7**7 / 4**9; 2.5 (= 5/2) = 0b10.1 = 0o2.4 = 0x2.8 3.141_567_230_224_609_375 (= 823543/262144) = 0b11.0010_0100_0011_1101_11 = 0o3.1103_67 = 0x3.243d_c > pi * r**2 19.634_795_188_903_808_593_75 (= 20588575/1048576) = 0b1_0011.1010_0010_1000_0001_1111 = 0o23.5050_076 = 0x13.a281_f .Ed .Pp The .Va _ variable is automatically assigned to the latest result: .Bd -literal -offset indent > 5 / 2 2.5 (= 5/2) = 0b10.1 = 0o2.4 = 0x2.8 > 2 * _ 5 = 0b101 = 0o5 = 0x5 .Ed .Sh VERSION This manual is written for .Nm 0.2.0. .Sh SEE ALSO .Xr bc 1 , .Xr dc 1 .Sh AUTHORS .Nm was created by .An Noah Hellman Aq Mt noah@hllmn.net