计算压岁钱

简单的算术

main.cpp
#include <iostream>

#include "mclog.h"
#include "byte.h"

int main(int argc, char **argv)
{
    // 计算今日花销
    int pay = 15 + 2 + 45 + 230;
    MCLOG("计算今日花销 " << $(pay))

    // 计算零花钱
    int father = 25;
    int mother = 200;
    int nuncle_2 = 15;
    int nuncle_3 = 88;
    int money = father + mother + nuncle_2 + nuncle_3;
    int money_save = money - 288;
    MCLOG("\n计算零花钱 " << $(money))
    MCLOG("\n被妈妈保存后的剩余零花钱 " << $(money_save))

    // 计算红包面积
    double width = 12.4;
    double length = 5.8;
    double area = width * length;
    MCLOG("\n计算红包面积 " << $(area))

    // 计算兄弟平分压岁钱-你的部分
    int sum_money = 9;
    int brothers = 2;
    int part = sum_money / brothers;
    MCLOG("\n计算兄弟平分压岁钱-你的部分 " << $(part))

    // 计算兄弟平分压岁钱-重新计算
    double re_sum_money = 9.0;
    double re_brothers = 2.0;
    double re_part = re_sum_money / re_brothers;
    MCLOG("\n计算兄弟平分压岁钱-重新计算 " << $(re_part))

    // 类型字节长度
    char int8 = 0;
    short int16 = 0;
    int int32 = 0;
    long long int64 = 0;
    float f32 = 0.0;
    double f64 = 0.0;

    MCLOG("\n类型字节长度")
    MCLOG($(sizeof(int8)))
    MCLOG($(sizeof(int16)))
    MCLOG($(sizeof(int32)))
    MCLOG($(sizeof(int64)))
    MCLOG($(sizeof(f32)))
    MCLOG($(sizeof(f64)))

    MCLOG("\n正负数类型最大长度")
    MCLOG($(INT8_MAX))
    MCLOG($(INT16_MAX))
    MCLOG($(INT32_MAX))
    MCLOG($(INT64_MAX))

    MCLOG("\n正整数各类型最大长度")
    MCLOG($(UINT8_MAX))
    MCLOG($(UINT16_MAX))
    MCLOG($(UINT32_MAX))
    MCLOG($(UINT64_MAX))

    /*
        正负数与正整数的最大值:
        下面的三个变量的值,int 需要首位 0 区分正负值
        0b01111111111111111111111111111111
        0b11111111111111111111111111111111
        0b11111111111111111111111111111111
    */
    int int32_max = 0b01111111111111111111111111111111;
    int int32_max_neg = 0b11111111111111111111111111111111;
    unsigned int uint32_max = 0b11111111111111111111111111111111;
    MCLOG("\n正负数与正整数的最大值")
    MCLOG($(int32_max))
    MCLOG($(int32_max_neg))
    MCLOG($(uint32_max))

    // 正数与负数
    int pos = 5;
    int neg = -5;
    std::string bit_pos = print_byte_int32(pos);
    std::string bit_neg = print_byte_int32(neg);
    MCLOG("\n正数 " << $(bit_pos))
    MCLOG("\n负数 " << $(bit_neg))

    // 整数与浮点二进制存储格式对比
    int iyear = 12;
    float fyear = 12.0;
    std::string bit_iyear = print_byte_int32(iyear);
    std::string bit_fyear = print_byte_float32(fyear);
    MCLOG("\n整数二进制 " << $(bit_iyear))
    MCLOG("\n浮点二进制 " << $(bit_fyear))

    // 额外的测试-下面提供可口算的二进制,请分析他们的值
    /*
        整数分析:
        1.高位第一位是0,是正数
        2.采用 1248 算法分析 0100 1100
            0   1  0  0  1 1 0 0
            128 64 32 16 8 4 2 1
        3.计算出公式 4 + 8 + 64 = 76
    */
    // 直接复制二进制bit数据
    int itest = 0b00000000000000000000000001001100;

    /*
        负数分析:
        1.高位第一位是1,是负数
        2.反转 1110 1011 为 0001 0100
        3.采用 1248 算法分析 0001 0100
            0   0  0  1  0 1 0 0
            128 64 32 16 8 4 2 1
        4.计算出公式 16 + 4 = 20
        5.结果加一后取反 -1 * (20 + 1) = -21
    */
    int itest_neg = 0b11111111111111111111111111101011;

    /*
        浮点分析:
        1.先将很分段为 符号|指数|尾数 的 1|8|23 结构,0 10000010 10011000000000000000000
        2.判断为正数,指数为 10000010,使用 1248 法分析,忽略头部后结果 + 1
            0 0 1 0
            8 4 2 1
            2 + 1 = 3
        3.获取指数为 2 ^ 3 = 8,
        4.继续分析尾数,尾数计算为逐步减半法,取得总数后 + 1
            1001 1000
            1 = 0.5
            0 = 0.25
            0 = 0.125
            1 = 0.0625
            1 = 0.03125
        5.尾数公式为 1 + 0.5 + 0.0625 + 0.03125 = 1.5937
        6.合并指数和尾数公式为 1 * 1.5937 * 8 = 12.75
    */
    // 二进制只能赋值给整数,通过 memcpy 进行内存拷贝,使它们的二进制数据一致
    int iftest = 0b01000001010011000000000000000000;
    float ftest = 0.0;
    std::memcpy(&ftest, &iftest, sizeof(ftest));

    MCLOG("\n验证结果")
    MCLOG($(itest))
    MCLOG($(itest_neg))
    MCLOG($(ftest))

    return 0;
}
byte.h
#ifndef BYTE_H
#define BYTE_H

// 引入字符串的STL头文件 - string
#include <string>

// 处理内存与字符的STL头文件 - memcpy
#include <cstring>

// 整数打印二进制函数
std::string print_byte_int32(int byte)
{
    // 准备字符串结果
    std::string ret;

    // 1byte 等于 8bit,循环次数为 sizeof(byte) * 8,字节数乘8
    // byte的类型是int,4byte,4*8=32,这个循环会执行32次,
    for (int i = 0; i < sizeof(byte) * 8; i++)
    {
        // 根据第一位是1,加入1,第一位是0,加入0
        if (byte & 0x01)
        {
            ret.push_back('1');
        }
        else
        {
            ret.push_back('0');
        }

        // 将第二位推到第一位,第一位消失
        byte >>= 1;
    }

    // 反转字符串顺序
    ret = std::string(ret.rbegin(), ret.rend());

    // 返回字符串结果
    return ret;
}

// 浮点数打印二进制函数
std::string print_byte_float32(float byte)
{
    // 使用 memcpy 将浮点数转移到整数类型
    int ibyte = 0;
    std::memcpy(&ibyte, &byte, sizeof(ibyte));

    // 调用整数打印二进制函数
    return print_byte_int32(ibyte);
}

#endif // BYTE_H
打印结果
计算今日花销 [pay: 292]  [/home/red/open/github/mcpp/example/04/main.cpp:10]

计算零花钱 [money: 328]  [/home/red/open/github/mcpp/example/04/main.cpp:19]

被妈妈保存后的剩余零花钱 [money_save: 40]  [/home/red/open/github/mcpp/example/04/main.cpp:20]

计算红包面积 [area: 71.92]  [/home/red/open/github/mcpp/example/04/main.cpp:26]

计算兄弟平分压岁钱-你的部分 [part: 4]  [/home/red/open/github/mcpp/example/04/main.cpp:32]

计算兄弟平分压岁钱-重新计算 [re_part: 4.5]  [/home/red/open/github/mcpp/example/04/main.cpp:38]

类型字节长度 [/home/red/open/github/mcpp/example/04/main.cpp:48]
[sizeof(int8): 1]  [/home/red/open/github/mcpp/example/04/main.cpp:49]
[sizeof(int16): 2]  [/home/red/open/github/mcpp/example/04/main.cpp:50]
[sizeof(int32): 4]  [/home/red/open/github/mcpp/example/04/main.cpp:51]
[sizeof(int64): 8]  [/home/red/open/github/mcpp/example/04/main.cpp:52]
[sizeof(f32): 4]  [/home/red/open/github/mcpp/example/04/main.cpp:53]
[sizeof(f64): 8]  [/home/red/open/github/mcpp/example/04/main.cpp:54]

正负数类型最大长度 [/home/red/open/github/mcpp/example/04/main.cpp:56]
[INT8_MAX: 127]  [/home/red/open/github/mcpp/example/04/main.cpp:57]
[INT16_MAX: 32767]  [/home/red/open/github/mcpp/example/04/main.cpp:58]
[INT32_MAX: 2147483647]  [/home/red/open/github/mcpp/example/04/main.cpp:59]
[INT64_MAX: 9223372036854775807]  [/home/red/open/github/mcpp/example/04/main.cpp:60]

正整数各类型最大长度 [/home/red/open/github/mcpp/example/04/main.cpp:62]
[UINT8_MAX: 255]  [/home/red/open/github/mcpp/example/04/main.cpp:63]
[UINT16_MAX: 65535]  [/home/red/open/github/mcpp/example/04/main.cpp:64]
[UINT32_MAX: 4294967295]  [/home/red/open/github/mcpp/example/04/main.cpp:65]
[UINT64_MAX: 18446744073709551615]  [/home/red/open/github/mcpp/example/04/main.cpp:66]

正负数与正整数的最大值 [/home/red/open/github/mcpp/example/04/main.cpp:78]
[int32_max: 2147483647]  [/home/red/open/github/mcpp/example/04/main.cpp:79]
[int32_max_neg: -1]  [/home/red/open/github/mcpp/example/04/main.cpp:80]
[uint32_max: 4294967295]  [/home/red/open/github/mcpp/example/04/main.cpp:81]

正数 [bit_pos: 00000000000000000000000000000101]  [/home/red/open/github/mcpp/example/04/main.cpp:88]

负数 [bit_neg: 11111111111111111111111111111011]  [/home/red/open/github/mcpp/example/04/main.cpp:89]

整数二进制 [bit_iyear: 00000000000000000000000000001100]  [/home/red/open/github/mcpp/example/04/main.cpp:96]

浮点二进制 [bit_fyear: 01000001010000000000000000000000]  [/home/red/open/github/mcpp/example/04/main.cpp:97]

验证结果 [/home/red/open/github/mcpp/example/04/main.cpp:146]
[itest: 76]  [/home/red/open/github/mcpp/example/04/main.cpp:147]
[itest_neg: -21]  [/home/red/open/github/mcpp/example/04/main.cpp:148]
[ftest: 12.75]  [/home/red/open/github/mcpp/example/04/main.cpp:149]

代码依旧很长啊,请先简单浏览一下代码,后续会进行解释
今天来讲一讲如何使用代码计算,上面的代码简单的样式了数学中加减乘除的用法,用于计算一些问题,上面的代码的每一处计算都是有实际需求的,这个系列的文章并不会列举出所有的计算符号与功能,只会举例子对功能进行说明,如果你需要更详细的资料请自行使用AI查询
C++中需要运行计算代码,离不开变量,变量是一种存储数据的容器,这种数据可以是任何东西,如数字/字符/地址等等,需要将他们存入变量,然后使用运算符进行计算,计算的结果依旧在变量中,因为结果也需要使用变量来存储
请记住在编程中,所有的操作都需要借助变量

变量与字面量
int pay = 15 + 2 + 45 + 230;
MCLOG("计算今日花销 " << $(pay))

代码中我们打印了今日花销,其中 pay 就是变量,而 15 + 2 + 45 + 230 这一大段的内容叫字面量,他们是不可改变的数字,他们之间的 = + 赋值号和加号都是操作符号,我们需要借助操作符来进行运算
变量顾名思义就是可变的东西,而字面量是不可变的,我们将字面量运算的结果都存入了变量,
整个运算过程是从左往右第一个 + 开始的,当他们累加完成之后,执行 = ,将内容赋值到 pay 变量中,然后打印 pay 变量就获取到了结果

这不是等于号
// 计算零花钱
int father = 25;
int mother = 200;
int nuncle_2 = 15;
int nuncle_3 = 88;
int money = father + mother + nuncle_2 + nuncle_3;
int money_save = money - 288;
MCLOG("\n计算零花钱 " << $(money))
MCLOG("\n被妈妈保存后的剩余零花钱 " << $(money_save))

上面代码中,我们将收集到的零花钱做了计算,请注意,我把零花钱数字赋值到了 father father 等变量中
请记住 = 是赋值号,而不是等于号,这是反数学常识的,在C++中规定,赋值号是将右边的数据赋值到左边
变量名表示是从谁手里拿到的钱,然后在下面进行运算得到 money 变量表示我的所有零花钱总数,这里可以看出和计算开销代码的区别,这里的 + 操作符号是对变量进行运算的,而不是用数字表示的字面量,在C++中运算操作符号是可以对 基本数据类型 直接进行运算的

变量命名
int indexBook = 0;      // 小驼峰
int index_book = 0;     // 下划线

在计算零花钱中,我进行了多次的赋值操作,生成了很多变量,出现了 nuncle_2 nuncle_3 等名称,对二叔三叔进行区分,这里我使用的是前缀式下划线命名规则,这种命名规则总是将相同部分提取到前面,然后断句时需要使用下划线进行分割,这种前缀命名的好处是可以快速进行代码补全
你可能还见过用大写来命名的方式,通常他们把大写和下划线命名为 驼峰/蛇形 匈牙利/下划线 命名法之类的称呼,命名方式会影响到项目/函数/变量/类名称等方方面面,命名方式需要遵守已有项目规范,保持代码的统一风格
统一风格的命名是 编程规范 的重要一步
你可能已经发现,上面众多代码中并没有出现类型 a b c 等变量,每一个命名都是有对应含义的且可推敲的,因为这种无意识的随意起名的变量名称会严重破坏新手的命名习惯,如果你还是新手,请保持有目的命名的习惯

检查小数点
// 计算兄弟平分压岁钱-你的部分
int sum_money = 9;
int brothers = 2;
int part = sum_money / brothers;
MCLOG("\n计算兄弟平分压岁钱-你的部分 " << $(part))

// 计算兄弟平分压岁钱-重新计算
double re_sum_money = 9.0;
double re_brothers = 2.0;
double re_part = re_sum_money / re_brothers;
MCLOG("\n计算兄弟平分压岁钱-重新计算 " << $(re_part))

// 打印
计算兄弟平分压岁钱-你的部分 [part: 4] 
计算兄弟平分压岁钱-重新计算 [re_part: 4.5] 

你发现了吗,计算兄弟平分压岁钱中,有9块钱,两兄弟,但是计算结果是你只能分到4块,而不是4.5块
在C++中,整数类型是不能存储小数点的,所以0.5直接就消失了,你如果需要存储小数点,那就需要使用浮点类型进行计算,在编程中带小数数字称为浮点数
整数和浮点数的存储方式和计算方式都是不一样的,下面将会讲述他们的区别,并让你了解变量的本质是什么,是如何存储数据的

内存结构

基本数据类型
// 整数类型 : 字节大小
char        : 1
short       : 2
int         : 4
long long   : 8

// 浮点类型
float       : 4
double      : 8

上面是C++中常见的几种数据类型,提供了 1~8 个字节(byte)的空间来存储数据,不同字节长度可以存储的最大数字不同,他们的最大数可以在打印结果中看到
通常使用 int 进行存储,即使你只需要存数据 1,也推荐使用 int,只要数据超过 int 的最大值21亿左右
整数中当存储数字超出最大值,超出部分会字节截断,存储的结果会发生错误,你需要考虑更大字节的类型
最常用的 int 为 4byte 字节,1byte 等于 8bit 比特,所以一个 int 为 32bit,可以存储的最大数值是 2^31 2的31次方,值为 2147483647 ,21亿左右
为什么 int 最大值是31次方,不是32次方呢,因为 int 可以存储负数,整数需要使用最高位的 1bit 来区分正负值
int 类型很常用,如果你在游戏中发现某些数值的上限是21亿,说明这个数值使用了 int 类型

正整数与正负数
// 正负数与正整数的最大值:
int int32_max = 0b01111111111111111111111111111111;
int int32_max_neg = 0b11111111111111111111111111111111;
unsigned int uint32_max = 0b11111111111111111111111111111111;
MCLOG("\n正负数与正整数的最大值")
MCLOG($(int32_max))
MCLOG($(int32_max_neg))
MCLOG($(uint32_max))

// 打印
[int32_max: 2147483647] 
[int32_max_neg: -1] 
[uint32_max: 4294967295] 

整数区分正负数和正整数两种类型,他们的区别是能否可以存储负数,正负数是可以存储负数的,正整数则不能,如果你给正整数传入负数,它会自己转为正数
正负数和正整数通常被称作有符号整数和无符号整数,但我习惯用正负来区分,而不是符号
从打印结果中你会发现,正整数比正负数的最大值要大整整一倍,因为正负数 int 的值是 2^31,正整数 unsigned int 的值是 2^32,它无需使用 1bit 来区分正负值,增加 1bit 在二进制数值上刚好是一倍

类型字节示意图

在这里插入图片描述

图中标出了常用的基本数据类型,他们在字节大小上,每提升一个等级都是上一个类型的一倍,在可存储的最大值也会是指数级提升

有意思的负数
// 正数与负数
int pos = 5;
int neg = -5;
std::string bit_pos = print_byte_int32(pos);
std::string bit_neg = print_byte_int32(neg);

// 打印
正数 [bit_pos: 00000000000000000000000000000101]  
负数 [bit_neg: 11111111111111111111111111111011] 

这段代码使用 print_byte_int32 函数打印了 pos neg 变量的二进制,print_byte_int32 函数是自定义的,它放在了 common/byte.h 文件中,相信你已经了解过如何将不同功能,划分到对应的头文件中,不过现在我并不打算深究这个函数
我们来看正数和负数的二进制规则,5和-5之间其实差别并没有很大,但是在二进制上看起来却差的非常多,看起来负数是被反着存的
如果将-5的负责反转的会的到 0100 等于 4,这个值 +1 刚好是5,所有的负数的存储都遵守了这个规则
反着存关键的地方是可以将负数的减法运算变成加法,这是计算机电路逻辑运算的优化,很有意思的点,需要你自行了解
分析整数二进制可以使用 1248 法,在代码上就简单的例子可以参考,这方面的知识需要自行了解

整数与浮点数的存储
// 整数与浮点二进制存储格式对比
int iyear = 12;
float fyear = 12.0;
std::string bit_iyear = print_byte_int32(iyear);
std::string bit_fyear = print_byte_float32(fyear);
MCLOG("\n整数二进制 " << $(bit_iyear))
MCLOG("\n浮点二进制 " << $(bit_fyear))

// 打印
整数二进制 [bit_iyear: 00000000000000000000000000001100] 
浮点二进制 [bit_fyear: 01000001010000000000000000000000] 

从打印结果上看,都是数字 12,但是存在整数和浮点数上,它们在内存中的二进制排列却非常不一样,那是因为整数和浮点数对数据的计算方式不一样导致的
整数是通过 2^n 1248 的指数提升累加的方式,将二进制转为数字
浮点数是根据 IEEE 754 标准定制浮点数的 将二进制分为 符号|指数|尾数 的结构,然后通过公式进行计算得到数字
整数和浮点数从二进制推算成数字的过程在代码中有简单示例,如果你想简单了解可以看一看,更具体的推算需要你自行了解

只是二进制而以

C++中存在各种数据结构,各种类型,其实它们都只是一段存储在变量内存中的二进制而已,它们可以相互转换,从而变成不一样的数据
你要记住,在计算机中只是二进制,不存在 整数/浮点数/对象/指针 等东西,这些类型只是它们对一块保存着二进制内容的地址,根据不同的类型采用不同的计算方式罢了
如果可以,你完全可以把上面的浮点数采用整数的计算方式,从而得到不一样的数字,这一段只是想告诉你,在C++中不要在意具体类型,你应该要关注不同类型的运行规则

尝试推断数字

到这里你应该已经明白了,在C++中一个整数或者浮点数是如何通过运算符进行运算的,也知道了它们在变量内存中存储的方式与计算规则,请你尝试去看看代码中通过二进制反推数字的过程,并理解它们的计算方式
要注意的是浮点数 double float 的计算方法是不一样的,不过整数类型都是可以使用 1248 法进行计算的
例子提到了 int 和 float 的技术方式,是因为它们在字节长度是一样的,你可以更直观感受到不同计算方式对结果的影响

项目路径

https://github.com/HellowAmy/mcpp.git
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐