精华内容
下载资源
问答
  • 二叉树数值表达式

    2020-10-22 12:41:17
    二叉树数值表达式 问题描述 给定一个二叉树,这个二叉树对应一个有效的数值表达式。树的每一个叶子结点都是一个整数,非叶子结点都是加 + 减 - 乘 * 除 \ 这几个运算符之一。算法返回表达式计算的结果。 测试样例 # ...

    Arithmetic Binary Tree

    二叉树数值表达式

    问题描述
    给定一个二叉树,这个二叉树对应一个有效的数值表达式。树的每一个叶子结点都是一个整数,非叶子结点都是加 + 减 - 乘 * 除 \ 这几个运算符之一。算法返回表达式计算的结果。

    测试样例

    # Input: 
        *
       / \
      +    +
     / \  / \
    3  2  4  5
    # Output:
    45
    # 该表达式为 (3 + 2) * (4 + 5) = 45
    

    内容首发于微信公众号IT信息教室,如果您想学习更多AI相关的技能,欢迎搜索关注或微信扫描下方二维码关注~~

    在这里插入图片描述

    参考代码

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    class Node:
        def __init__(self, value):
            self.value = value
            self.left = None
            self.right = None
    
    # Type of operations        
    PLUS = '+'
    MINUS = '-'
    TIMES = '*'
    DIVIDE = '/'
    
    # Main function
    # O(N) time, O(log(n)) space
    # 这是一个简单的递归,对于遍历到的任意个结点
    # 如果结点值是运算符,那么递归调用主函数分别判断其左右子树
    # 如果结点值是数值,那么就返回该值
    def evaluate(root):
        if root.value == PLUS:
            return evaluate(root.left) + evaluate(root.right)
        elif root.value == MINUS:
            return evaluate(root.left) + evaluate(root.right)
        elif root.value == TIMES:
            return evaluate(root.left) * evaluate(root.right)
        elif root.value == DIVIDE:
            return evaluate(root.left) / evaluate(root.right)
        else:
            return root.value
    
    # Test Program
    tree = Node(TIMES)
    tree.left = Node(PLUS)
    tree.right = Node(PLUS)
    tree.left.left = Node(3)
    tree.left.right = Node(2)
    tree.right.left = Node(4)
    tree.right.right = Node(5)
    
    result = evaluate(tree)
    print(result)
    # 45
    # 二叉树对应的表达式为 (3 + 2) * (4 + 5) = 45
    
    展开全文
  • Python:数值表达式

    2019-04-12 20:52:55
    数值表达式 三大数据类别: 类型 类型名(type()) 整数 int() 浮点数 float() 复数 complex() 算术运算符: + - * / // % ** 加/正号 减/负号 乘 ...

    数值表达式

    三大数据类别:
    类型 类型名(type())
    整数 int()
    浮点数 float()
    复数 complex()
    算术运算符:
    + - * / // % **
    加/正号 减/负号 整除 求余数 乘幂

    浮点数计算有误差

    类型转换
    • 整数和浮点数都有自己的乘法运算规则,如果遇到表达式要求做一个整数和一个浮点数的运算,那么就先将整数转化成与之等值的浮点数对象,再按照浮点数的运算规则计算。
    • 但是整数可以任意大,具有精度,用浮点数表示,或许能表示成其近似值,甚至无法表示(整数太大而溢出)。

    强制类型转换:

    >>>int(2.5)
    2
    >>>float(5)
    5.0
    
    复数(complex number)

    数学里通常写成 a + bi
    在python中 用 jJ来代替 i
    **e.g. 1 + 2j, 1.2 + 3.0J

    tip

    在用python写代码时,由于浮点数精度问题,在进行处罚运算时,能运用 // 就不用 /

    展开全文
  • Java数值表达式

    2015-01-29 11:19:41
    数值表达式... 2 1.奇偶判断... 2 2.小数精确计算... 2 3.int整数相乘溢出... 3 4.负的十六进制与八进制字面常量... 3 5.窄数字类型提升至宽类型时使用符号位扩展还是零扩展... 4 6.((byte)0x90 =...


    数值表达式... 2
    1.奇偶判断... 2
    2.小数精确计算... 2
    3.int整数相乘溢出... 3
    4.负的十六进制与八进制字面常量... 3
    5.窄数字类型提升至宽类型时使用符号位扩展还是零扩展... 4
    6.((byte)0x90 == 0x90)?. 5
    7.三元表达式(?:)... 5
    8.+=复合赋值问题... 6
    9.i =++i;与i=i++;的区别... 7
    10.Integer.MAX_VALUE + 1=?. 8
    11.-1<<32=?、-1<<65=?. 8
    12.一个数永远不会等于它自己加1吗?i==i+1. 9
    13.自己不等于自己吗?i!=i10
    14.自动拆箱... 10
    15.为什么-0x00000000==0x00000000、-0x80000000== 0x80000000. 10
    16.Math.abs结果一定为非负数吗?. 12
    17.不要使用基于减法的比较器... 12
    18.int i=-2147483648与int i=-(2147483648)?. 13

    数值表达式

    1.      奇偶判断


    不要使用 i % 2 == 1 来判断是否是奇数,因为i为负奇数时不成立,请使用 i % 2 != 0 来判断是否是奇数,或使用高效式 (i & 1) != 0来判断。

    2.      小数精确计算


    System.out.println(2.00 -1.10);//0.8999999999999999
    上面的计算出的结果不是 0.9,而是一连串的小数。问题在于1.1这个数字不能被精确表示为一个double,因此它被表示为最接近它的double值,该程序从2中减去的就是这个值,但这个计算的结果并不是最接近0.9的double值。
    一般地说,问题在于并不是所有的小数都可以用二进制浮点数精确表示。
    二进制浮点对于货币计算是非常不适合的,因为它不可能将1.0表示成10的其他任何负次幂。
     
    解决问题的第一种方式是使用货币的最小单位(分)来表示:System.out.println(200-110);//90
     
    第二种方式是使用BigDecimal,但一定要用BigDecimal(String)构造器,而千万不要用BigDecimal(double)来构造(也不能将float或double型转换成String再来使用BigDecimal(String)来构造,因为在将float或double转换成String时精度已丢失)。例如new BigDecimal(0.1),它将返回一个BigDecimal,也即0.1000000000000000055511151231257827021181583404541015625,正确使用BigDecimal,程序就可以打印出我们所期望的结果0.9:
    System.out.println(newBigDecimal("2.0").subtract(newBigDecimal("1.10")));// 0.9
     
    另外,如果要比较两个浮点数的大小,要使用BigDecimal的compareTo方法。

    3.      int整数相乘溢出


    我们计算一天中的微秒数:
    long microsPerDay = 24 * 60 * 60 * 1000 * 1000;// 正确结果应为:86400000000
    System.out.println(microsPerDay);// 实际上为:500654080
     
    问题在于计算过程中溢出了。这个计算式完全是以int运算来执行的,并且只有在运算完成之后,其结果才被提升为long,而此时已经太迟:计算已经溢出。
     
    解决方法使计算表达式的第一个因子明确为long型,这样可以强制表达式中所有的后续计算都用long运算来完成,这样结果就不会溢出:
    long microsPerDay = 24L * 60 * 60 * 1000 * 1000;

    4.      负的十六进制与八进制字面常量


    “数字字面常量”的类型都是int型,而不管他们是几进制,所以“2147483648”、“0x180000000(十六进制,共33位,所以超过了整数的取值范围)”字面常量是错误的,编译时会报超过int的取值范围了,所以要确定以long来表示“2147483648L”、“0x180000000L”。
     
    十进制字面常量只有一个特性,即所有的十进制字面常量都是正数,如果想写一个负的十进制,则需要在正的十进制字面常量前加上“-”即可。
     
    十六进制或八进制字面常量可就不一定是正数或负数,是正还是负,则要根据当前情况看:如果十六进制和八进制字面常量的最高位被设置成了1,那么它们就是负数:
    System.out.println(0x80);//128
    //0x81看作是int型,最高位(第32位)为0,所以是正数
    System.out.println(0x81);//129
    System.out.println(0x8001);//32769
    System.out.println(0x70000001);//1879048193
    //字面量0x80000001为int型,最高位(第32位)为1,所以是负数
    System.out.println(0x80000001);//-2147483647
    //字面量0x80000001L强制转为long型,最高位(第64位)为0,所以是正数
    System.out.println(0x80000001L);//2147483649
    //最小int型
    System.out.println(0x80000000);//-2147483648
    //只要超过32位,就需要在字面常量后加L强转long,否则编译时出错
    System.out.println(0x8000000000000000L);//-9223372036854775808
     
    从上面可以看出,十六进制的字面常量表示的是int型,如果超过32位,则需要在后面加“L”,否则编译过不过。如果为32,则为负int正数,超过32位,则为long型,但需明确指定为long。
     
     
    System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));// cafebabe
    结果为什么不是0x1cafebabe?该程序执行的加法是一个混合类型的计算:左操作数是long型,而右操作数是int类型。为了执行该计算,Java将int类型的数值用拓宽原生类型转换提升为long类型,然后对两个long类型数值相加。因为int是有符号的整数类型,所以这个转换执行的是符号扩展。
    这个加法的右操作数0xcafebabe为32位,将被提升为long类型的数值0xffffffffcafebabeL,之后这个数值加上了左操作0x100000000L。当视为int类型时,经过符号扩展之后的右操作数的高32位是1,而左操作数的第32位是1,两个数值相加得到了0:
    0x ffffffffcafebabeL
    +0x 0000000100000000L
    -----------------------------
    0x 00000000cafebabeL
     
    如果要得到正确的结果0x1cafebabe,则需在第二个操作数组后加上“L”明确看作是正的long型即可,此时相加时拓展符号位就为0:
    System.out.println(Long.toHexString(0x100000000L + 0xcafebabeL));// 1cafebabe

    5.      窄数字类型提升至宽类型时使用符号位扩展还是零扩展


    System.out.println((int)(char)(byte)-1);// 65535
    结果为什么是65535而不是-1?
     
    窄的整型转换成较宽的整型时符号扩展规则:如果最初的数值类型是有符号的,那么就执行符号扩展(即如果符号位为1,则扩展为1,如果为零,则扩展为0);如果它是char,那么不管它将要被提升成什么类型,都执行零扩展。
     
    了解上面的规则后,我们再来看看迷题:因为byte是有符号的类型,所以在将byte数值-1(二进制为:11111111)提升到char时,会发生符号位扩展,又符号位为1,所以就补8个1,最后为16个1;然后从char到int的提升时,由于是char型提升到其他类型,所以采用零扩展而不是符号扩展,结果int数值就成了65535。
     
    如果将一个char数值c转型为一个宽度更宽的类型时,只是以零来扩展,但如果清晰表达以零扩展的意图,则可以考虑使用一个位掩码:
    int i = c & 0xffff;//实质上等同于:int i = c ;
     
    如果将一个char数值c转型为一个宽度更宽的整型,并且希望有符号扩展,那么就先将char转型为一个short,它与char上个具有同样的宽度,但是它是有符号的:
    int i = (short)c;
     
    如果将一个byte数值b转型为一个char,并且不希望有符号扩展,那么必须使用一个位掩码来限制它:
    char c = (char)(b & 0xff);// char c = (char) b;为有符号扩展

    6.      ((byte)0x90 == 0x90)?


    答案是不等的,尽管外表看起来是成立的,但是它却等于false。为了比较byte数值(byte)0x90和int数值0x90,Java通过拓宽原生类型将byte提升为int,然后比较这两个int数值。因为byte是一个有符号类型,所以这个转换执行的是符号扩展,将负的byte数值提升为了在数字上相等的int值(10010000à111111111111111111111111 10010000)。在本例中,该转换将(byte)0x90提升为int数值-112,它不等于int数值的0x90,即+144。
    解决办法:使用一个屏蔽码来消除符号扩展的影响,从而将byte转型为int。
    ((byte)0x90 & 0xff)== 0x90

    7.      三元表达式(?:)


    char x = 'X';
    int i = 0;
    System.out.println(true ? x : 0);// X
    System.out.println(false ? i : x);// 88
     
    条件表达式结果类型的规则:
    (1)       如果第二个和第三个操作数具有相同的类型,那么它就是条件表达式的类型。
    (2)       如果一个操作的类型是T,T表示byte、short或char,而另一个操作数是一个int类型的“字面常量”,并且它的值可以用类型T表示,那条件表达式的类型就是T。
    (3)       否则,将对操作数类型进行提升,而条件表达式的类型就是第二个和第三个操作被提升之后的类型。
     
    现来使用以上规则解上面的迷题,第一个表达式符合第二条规则:一个操作数的类型是char,另一个的类型是字面常量为0的int型,但0可以表示成char,所以最终返回类型以char类型为准;第二个表达式符合第三条规则:因为i为int型变量,而x又为char型变量,所以会先将x提升至int型,所以最后的结果类型为int型,但如果将i定义成final时,则返回结果类型为char,则此时符合第二条规则,因为final类型的变量在编译时就使用“字面常量0”来替换三元表达式了:
    final int i = 0;
    System.out.println(false ? i : x);// X
     
    在JDK1.4版本或之前,条件操作符 ?: 中,当第二个和延续三个操作数是引用类型时,条件操作符要求它们其中一个必须是另一个的子类型,那怕它们有同一个父类也不行:
    public class T {
           public static void main(String[] args) {
                  System.out.println(f());
           }
           public static T f() {
                  // !!1.4不能编译,但1.5可以
                  // !!return true?new T1():new T2();
                  return true ? (T) new T1() : new T2();// T1
           }
    }
     
    class T1 extends T {
           public String toString() {
                  return "T1";
           }
    }
     
    class T2 extends T {
           public String toString() {
                  return "T2";
           }
    }
    在5.0或以上版本中,条件操作符在第二个和第三个操作数是引用类型时总是合法的。其结果类型是这两种类型的最小公共超类。公共超类总是存在的,因为Object是每一个对象类型的超类型,上面的最小公共超类是T,所以能编译。

    8.      +=复合赋值问题


    x+=i与x=x+i等效吗,许多程序员都会认为第一个表达式x+=i只是第二个表达式x=x+i的简写方式,但这并不准确。
     
    Java语言规范中提到:复合赋值 E1 op= E2等价于简单赋值 E1 = (T)((E1) op (E2)),其中T是E1的类型。
     
    复合赋值表达式自动地将所执行计算的结果转型为其左侧变量的类型。如果结果的类型与该变量的类型相同,那么这个转型不会造成任何影响,然而,如果结果的类型比该变量的类型要宽,那么复合赋值操作符将悄悄地执行一个窄化原生类型转换,这样就会导致结果不正确:
    short x=0;
    int i = 123456;
    x +=i;
    System.out.println(x);//-7616
     
    使用简单的赋值方式就不会有这样的问题了,因为宽类型不能自动转换成窄的类型,编译器会报错,这时我们就会注意到错误:x = x + i;//编译通不过
     
    请不要将复合赋值操作符作用于byte、short或char类型的变量;在将复合赋值操作符作用于int类型的变量时,要确保表达式右侧不是long、float或double类型;在将复合赋值操作符作用于float类型的变量时,要确保表达式右侧不是double类型。其实一句:不要将让左侧的类型窄于右侧的数字类型。
     
    总之,不要在short、byte或char类型的变量之上使用复合赋值操作符,因为这一过程会伴随着计算前类型的提升与计算后结果的截断,导致最后的计算结果不正确。

    9.      i =++i;与i=i++;的区别


    int i = 0;
    i = i++;
    System.out.println(i);
    上面的程序会输出什么?大部分会说是 1,是也,非也。运行时正确结果为0。
     
    i=++i;相当于以下二个语句(编译时出现警告,与i=i;警告相同):
    i=i+1;
    i=i;
     
    i = i++;相当于以下三个语句:
    int tmp = i;
    i = i + 1;
    i = tmp;
     
    下面看看下面程序片段:
    int i = 0, j = 0, y = 0;
    i++;//相当于:i=i+1;
    System.out.println("i=" + i);// i=1
    ++i;//相当于:i=i+1;
    System.out.println("i=" + i);// i=2
    i = i++;//相当于:int tmp=i;i=i+1;i=tmp;
    System.out.println("i=" + i);// i=2
    i = ++i;//编译时出现警告,与i=i;警告相同。相当于:i=i+1;i=i;
    System.out.println("i=" + i);// i=3
    j = i++;//相当于:int tmp=i;i=i+1;j=tmp;
    System.out.println("j=" + j);// j=3
    System.out.println("i=" + i);// i=4
    y = ++i;//相当于:i=i+1;y=i;
    System.out.println("y=" + y);// y=5
    System.out.println("i=" + i);// i=5

    10.Integer.MAX_VALUE + 1=?


    System.out.println(Integer.MAX_VALUE + 1);
    上面的程序输出多少?2147483647+1=2147483648?答案为-2147483648。
     
    查看源码Integer.MAX_VALUE 为MAX_VALUE = 0x7fffffff;所以加1后为0x80000000,又0x80000000为整型字面常量,满了32位,且最位为1,所以字面上等于 -0,但又由于 -0就是等于0,所以-0这个编码就规定为最小的负数,32位的最小负数就是-2147483648。

    11.-1<<32=?、-1<<65=?


    如果左操作数是int(如果是byte、short、char型时会提升至int型再进行位操作)型,移位操作符只使用其右操作数的低5位作为移位长度(也就是将右操作数除以32取余);如果左操作数是long型,移位操作符只使用其右操作数的低6位作为移位长度(也就是将右操作数除以64取余);
     
    再看看下面程序片段就会知道结果:
    System.out.println(-1 << 31);// -2147483648 向左移31%32=31位
    System.out.println(-1 << 32);// -1 向左移32%32=0位
    System.out.println(-1 << 33);// -2 向左移33%32=1位
    System.out.println(-1 << 1);// -2 向左移1%32=1位
     
    System.out.println(-1L << 63);// -9223372036854775808 向左移63%64=63位
    System.out.println(-1L << 64);// -1 向左移64%64=0位
    System.out.println(-1L << 65);// -2 向左移65%64=1位
    System.out.println(-1L << 1);// -2 向左移1%64=1位
     
    byte b = -1;// byte型在位操作前类型提升至int
    System.out.println(b << 31);// -2147483648 向左移31%32=31位
    System.out.println(b << 63);// -2147483648 向左移63%32=31位
     
    short s = -1;// short型在位操作前类型提升至int
    System.out.println(s << 31);// -2147483648 向左移31%32=31位
    System.out.println(s << 63);// -2147483648 向左移63%32=31位
     
    char c = 1;// char型在位操作前类型提升至int
    System.out.println(c << 31);// -2147483648 向左移31%32=31位
    System.out.println(c << 63);// -2147483648 向左移63%32=31位

    12.一个数永远不会等于它自己加1吗?i==i+1


    一个数永远不会等于它自己加1,对吗?如果数字是整型,则对;如果这个数字是无穷大或都是浮点型足够大(如1.0e40),等式就可能成立了。
     
    Java强制要求使用IEEE 754浮点数算术运算,它可以让你用一个double或float来表示无穷大。
     
    浮点型分为double型、float型。
     
    无穷分为正无穷与负无穷。
     
    无穷大加1还是无穷大。
     
    一个浮点数值越大,它和其后继数值之间的间隔就越大。
     
    对一个足够大的浮点数加1不会改变它的值,因为1不足以“填补它与其后者之间的空隙”。
     
    浮点数操作返回的是最接近其精确数学结果的浮点数值。
     
    一旦毗邻的浮点数值之间的距离大于2,那么对其中的一个浮点数值加1将不会产生任何效果,因为其结果没有达到两个数值之间的一半。对于float类型,加1不会产生任何效果的最小数是2^25,即33554432;而对于double类型,最小数是2^54,大约是1.8*10^16。
     
    33554432F转二进制过程:
    33554432的二进制为:10000000000000000000000000,将该二进制化成规范的小数二进制,即小数从右向左移25位1.0000000000000000000000000,化成浮点数二进制0,25+127, 00000000000000000000000 00(丢弃最后两位),即0, 10011000, 00000000000000000000000,最后的结果为1.00000000000000000000000*2^25
     
     
    毗邻的浮点数值之间的距离被称为一个ulp,它是最小单位(unit in the last place)的首字母缩写。在5.0版本中,引入了Math.ulp方法来计算float或double数值的ulp。
     
    二进制浮点算术只是对实际算术的一种近似。
     
    // 注,整型数不能被 0 除,即(int)XX/0运行时抛异常
    double i = 1.0 / 0.0;// 正无穷大
    double j = -1.0 / 0.0;// 负无穷大
    // Double.POSITIVE_INFINITY定义为:POSITIVE_INFINITY = 1.0 / 0.0;
    System.out.println(i + " " + (i == Double.POSITIVE_INFINITY));//Infinity true
    // Double.NEGATIVE_INFINITY定义为:NEGATIVE_INFINITY = -1.0 / 0.0;
    System.out.println(j + " " + (j == Double.NEGATIVE_INFINITY));//-Infinity true
    System.out.println(i == (i + 1));// true
    System.out.println(0.1f == 0.1);// false
    float f = 33554432;
    System.out.println(f + " " + (f==(f+1)));//3.3554432E7 true

    13.自己不等于自己吗?i!=i


    NaN(Not a Number)不等于任何数,包括它自身在内。
     
    double i = 0.0/0.0;可表示NaN。
     
    float和double类型都有一个特殊的NaN值,Double.NaN、Float.NaN表示NaN。
     
    如果一个表达式中产生了NaN,则结果为NaN。
     
    System.out.println(0.0 / 0.0);// NaN
    System.out.println(Double.NaN + " " + (Double.NaN == (0.0 / 0.0)));//NaN false

    14.自动拆箱


    // 為了兼容以前版本,1.5不會自動拆箱
    System.out.println(new Integer(0) == new Integer(0));// false
    // 1.4编译非法,1.5会自动拆箱
    System.out.println(new Integer(0) == 0);// true

    15.为什么-0x00000000==0x00000000、-0x80000000== 0x80000000


    十六进制(0X开头)、八进制(0开头)、以及二进制(0B开头,JDK1.7已开始支持)表示了内存中直接存储形式,这与十进制是有区别的。
     
    规则:为了取一个整数类型(十六进制、八进制或二进制)的负值,对每一位(即内存所存储的二进制内容)取反,然后再加1;但如果是对一个十进制数求负操作,实质上直接求这个十进制整数的补码即可,但也可以按照前面对十六进制数那样取负操作,只不过先要将这个十进制数转换为补码形式再按前面规则取反后加1。
     
    运用上面的规则,-0x00000000的运算过程:对0x00000000先取反得到0xffffffff,再加1,-0x00000000的最后结果就为 0xffffffff+1,其最后的结果还是0x00000000,所以-0x00000000 == 0x00000000。前面是对0x00000000求负的过程,如果是对0求负呢?按上面的规则,对0取负就是求-0的补码即可,根据后面的规则,-0的补码为0x00000000。不过也可采用上面对一个十六进制取负的规来求,先求0的十六进制形式0x00000000,再按前面的取负过程来即可得到对0取负的结果也为0x00000000。
     
    运用前面的规则,-0x80000000的运算过程:对0x80000000先取反得到0x7fffffff,再加1,-0x80000000的最后结果就为 0x7fffffff+1,其最后的结果还是0x80000000,即-0x80000000 == 0x80000000。前面是对0x80000000求负的过程,如果是对2147483648求负呢?按上面的规则,对2147483648取负就是求-2147483648的补码,并要所后面的规则,-2147483648的补码就是0x80000000。不过也可采用上面对一个十六进制取负的规来求,先求2147483648的十六进制形式0x80000000,再按前面的取负过程来即可得到对2147483648取负的结果也为0x80000000。
     
    所有的整数在内存中都是以补码形式存储的,只不过正数或零的补码为本身而已;如果是负数,则对其绝对值取反后加1即是内存中存储形式。求一个负数的补码有个比较简单的规则:先求这个数绝对值的原码,然后从该二进制最末向前开始找第一个为1的位置,最后将这个1之前的各位取反(包括最高位符号位0),其他位不变,最终所得的二进制就为这个负数的补码,也就是最终在内存中存储的形式。不过在找这个第一个为1时可能找不到或在最高位,比如-0,其绝对值为0(0x00000000),也有可能这个1在最高位,比如-2147483648,其绝对值为2147483648(0x80000000),如果遇到绝对值的原码为0x00000000或0x80000000的情况下补码直接就是0x00000000或0x80000000。
     
    上面是讲了原码到补码的转换规则,那怎么由补码到原码就简单了:如果补码是正的,则说明原码是正的,所以这时补码就是原码;如果补码是负的,则原码一定是负的,此时转换过程正好与从负原码到补码的转换过程相反,即先将补码减1,再取反,但符号位不要变,即永远为1。
     
    // 因为Integer.MIN_VALUE定义成了0xffffffff,所以根据上面的规则得到相等的结论
    System.out.println(Integer.MIN_VALUE == -Integer.MIN_VALUE);// true      
    /*
    *  0x80000000取反得0x7fffffff,再加1得0x80000000,因为负数是
    *  以补码形式存储于内存中的,所以推导出结果原码为:0x80000000,
    *  即为-0,又因为-0是等于0的,所以不需要-0这个编码位,否则就多了
    *  一个0x80000000编码位了,所以最后就规定0x80000000为最小负数
    */
    System.out.println(-0x80000000);// -2147483648
    /*
    *  0x7fffffff取反得0x80000000,再加1得0x80000001,因为负数是
    *  以补码形式存储于内存中的,所以推导出0x80000001的原码为:0xffffffff(取反时
    *,第一位为符号位不要变,还是1),所以最后的结果就为 -0x7fffffff = -2147483647
    */
    System.out.println(-0x7fffffff);// -2147483647
     
     
    另外,还发现有趣现象:最大整数加1后会等于最小整数:
    // MAX_VALUE = 0x7fffffff; MIN_VALUE = 0x80000000;
    System.out.println((Integer.MAX_VALUE + 1) == Integer.MIN_VALUE);// true
    // MIN_VALUE = 0x8000000000000000L; MIN_VALUE = 0x8000000000000000L;
    System.out.println((Long.MAX_VALUE + 1) == Long.MIN_VALUE);// true
     
    当然,-Byte. MIN_VALUE==Byte.MIN_VALUE、-Short.MIN_VALUE== Short.MIN_VALUE、-Long.MIN_VALUE== Long.MIN_VALUE,也是成立的。

    16.Math.abs结果一定为非负数吗?


    System.out.println(Math.abs(Integer.MIN_VALUE));// -2147483648
    上面的程序不会输出2147483648,而是-2147483648,为什么?
     
    其实我们看一下Math.abs源码就知道为什么了,源码:(a < 0) ? -a : a;,结合上面那个迷题,我们就发现-Integer.MIN_VALUE= Integer.MIN_VALUE,所以上面的答案就是最小整数自己。
     
    另外我们也可以从API文档看到对Math.abs()方法的解释:如果参数等于 Integer.MIN_VALUE的值(即能够表示的最小负 int值),则结果与该值相同且为负。
     
    所以Math.abs不能保证一定会返回非负结果。
     
    当然,Long.MIN_VALUE也是这样的。

    17.不要使用基于减法的比较器


    Comparator<Integer> c = new Comparator<Integer>() {
           public int compare(Integer i1, Integer i2) {
                  return i1 - i2;// 升序
           }
    };
    List<Integer> l = new ArrayList<Integer>();
    l.add(new Integer(-2000000000));
    l.add(new Integer(2000000000));
    Collections.sort(l, c);
    System.out.println(l);// [2000000000, -2000000000]
    上面程序的比较器是升序,结果却不是这样,比较时出现了什么问题?
     
    先看看下面程序片断:
    int x = -2000000000;
    int y = 2000000000;
    /*
    * -2000000000 即 -(01110111001101011001010000000000)
    * 的补码为:                10001000110010100110110000000000
    *
    * 计算过程使用竖式表示:
    * 10001000110010100110110000000000
    * 10001000110010100110110000000000
    * --------------------------------
    * 00010001100101001101100000000000
    *
    * 计算结果溢出,结果为294967296
    */
    System.out.println(x - y);// 294967296
     
    所以不要使用减法的比较器,除非能确保要比较的数值之间的距离永远不会大于Intger.MAX_VALUE。
     
    基于整型的比较器的实现一般使用如下的方式来比较:
    public int compare(Integer i1, Integer i2) {
           return (i1 < i2 ? -1 : (i1 == i2 ? 0 : 1));
    }

    18. int i=-2147483648与int i=-(2147483648)?


    int i=-(2147483648);
    编译通不过!为什么
     
    int字面常量2147483638只能作为一元负操作符的操作数来使用。
     
    类似的还有最大long:
    long i=–(9223372036854775808L);

    展开全文
  • Expression 用于在运行时计算数值表达式的Mac和iOS库
  • 这个是利用栈求数值表达式的一个新版本,功能是最完善的一个。ps:版权所有。呵呵
  • 具体内容:利用 Object C 和 Foundation 框架,利用栈实现数值表达式的 求值。 (要求不使用 NSExpression)。 例如:输入字符串: 10÷6×7+15-8 给出正确的计算值。或 输出"字符串不是数学表达式无法计算
  • 2500行代码实现高性能数值表达式引擎 南京都昌信息科技有限公司 袁永福 2018-9-23 Http://www.dcwriter.cn ◆◆前言 在一些高自由度的软件中,特别是报表之类的软件。需要让用户自定义数值表达式,比如定义"A...

    2500行代码实现高性能数值表达式引擎

    南京都昌信息科技有限公司 袁永福 2018-9-23

    Http://www.dcwriter.cn

     

    ◆◆前言

    在一些高自由度的软件中,特别是报表之类的软件。需要让用户自定义数值表达式,比如定义"A+B*C-D/E",然后再实际运行中把具体的A,B,C,D,E的值代入表达式运算。这能显著增加软件的运行时的可配置性,是一个值得广泛应用的软件功能。本文就说明了如何使用2500行C#代码实现一种高性能的数值运算表达式引擎。

    完整源代码下载页面https://github.com/dcsoft-yyf/DCSoft.Expression

     

    ◆◆ANTLR引擎

    提到数值表达式引擎,不得不提起Antlr,一个很著名的开源软件,能自动生成源代码来生成语法解析引擎,然后可以在这个语法解析引擎的基础上来实现运算表达式。

    笔者此前也在使用ANTLR相关的代码来实现了运算表达式引擎,包含了10000行C#源代码。不过代码晦涩难懂,改进更加麻烦。近期在处理一个包含大量表达式的场景时出现了性能问题,需要改进。

    ◆◆新表达式运算引擎

    现在作者抛弃了旧的基于ANTLR的引擎,构造了全新的表达式运算引擎,核心模块只有2500行C#代码,生成的程序集文件只有27KB,但运行速度提升了10倍。而且程序简单易懂,扩展性强。当然不再具备ANTLR的广泛的通用性,但足够应付作者遇到的应用场景了。

    新源代码中定义的主要类型有:

    DCConstExpressionItem

    常量表达式元素。

    DCExpression

    表达式对象,为顶级API类型。

    DCExpressionItem

    抽象的表达式元素类型。所有的表达式元素都是从这个类型派生出来的。

    DCExpressionItemList

    表达式元素列表。

    DCFunctionExpressionItem

    函数调用表达式元素。

    DCGroupExpressionItem

    表达式元素组。

    DCOperatorExpressionItem

    运算操作符表达式元素。

    DCToken

    表达式语法中的标识符对象。

    DCTokenList

    标识符列表

    DCVariableExpressionItem

    变量表达式元素。

    IDCExpressionContext

    运行表达式时的上下文对象接口。

    新引擎工作过程如下:

    ◆第一步,表达式字符串符号解析

    其代码定义在DCToken.cs中。在此处,将字符分为以下几种:

    标识符

    包括0到9的数字字符,英文字母字符,$符号,其他标识符类型的字符。

    操作符

    包括"+-*/%\"字符。

    逻辑运算符

    包括"&^|=><"字符。

    分组开始字符

    为字符"("。

    分组结束字符

    为字符")"。

    所有相邻的同类型的字符合并在一起成为一组符号,此外还识别单引号或双引号作为边界的字符串常量。

    以下是符号解析的例子:

    原始文本

    解析结果

    A+B

    "A" , "+" , "B"

    A+SIN(B)*99

    "A" , "+" , "SIN" , "(" , "B" , ")" , "*" ,"99"

    A+B>98 && C<10

    "A" , "+" , "B" , ">" , "98" , "&&" , "C" , "<" , "10"

    体重/(身高*身高)

    "体重" , "/" , "(" , "身高" , "*" , "身高" , ")"

    ◆第二步,解析表达式单元元素

    其代码定义在DCExpression.cs中的 Parse()/ParseItem()函数中。将一个个标识符转换为表达式单元元素。目前支持以下元素类型:

    DCConstExpressionItem

    常量元素类型。分为字符串/数字/布尔值三种类型。

    DCFunctionExpressionItem

    函数元素类型。用于调用外部函数。

    DCGroupExpressionItem

    分组元素类型。由一对圆括号定义的子单元元素组合。

    DCOperatorExpressionItem

    操作符元素。

    DCVariableExpressionItem

    变量元素类型。由外部传入具体的变量值。

    解析过程是一种递归操作,用于构造出一个表达式元素树状结构。

    首先定义一个DCGroupExpressionItem作为根元素。可以看作把表达式的最外头放了一组圆括号。比如把"A+B"当作"(A+B)"。

    在这个过程中,遇到标识符并且紧跟着的是"("则认为是函数元素;遇到标识符"true"或"false"则认为是布尔类型的常量元素;遇到可以转换为数字的标识符则认为是数字常量元素;遇到其他标识符则认为是变量元素;遇到操作标识符则认为是操作符元素类型;遇到"("则为分组元素类型并递归解析子元素;遇到")"则认为结束分组元素类型,结束当前递归。此外还解析出字符串常量元素。

    经过解析操作,构造了只有一个根节点的表达式元素树状结构。以下是几个例子:

    A+B

    A+B*SIN(C+D)

    体重/(身高*身高)

    ◆第三步,优先级调整

    数值运算有优先级。规则是数学运算高于逻辑运算,乘除运算高于加减运算,圆括号可以改变运算优先级。

    此时需要将一长串的表达式元素按照优先级在此次整理。主要代码在DCExpression.cs中的CollpaseItems()中,作者称之为表达式元素列表的收缩。其过程如下:

    首先是特别处理减号元素的处理。当减号处于当前组的第一的位置,或者其前面是逻辑运算符时,则将减号元素转换为取负元素。

    然后对元素列表进行一个不定次数的循环。在循环体中,找到优先级最高的而且未经收缩的操作符元素,然后吞并左边和右边的元素。如此循环直到没法产生吞并操作为止。

    举个例子,对于表达式"A+B*C-D",则生成的表达式元素有

    在第一次循环中,*号是优先级最高的,则进行收缩处理,处理后的结构如下:

    上图中黄色元素表示该元素收缩处理过了,不再参与处理。

    在第二次循环中,"+"号优先级最高(后面的"-"和它是同等级的),则对它进行收缩,结果如下:

    在第三次循环中,"-"号优先级最高,则进行收缩,结果如下:

    之后根节点下再无可以处理的运算符元素,则退出循环。

    经过上述步骤,最为关键的表达式元素树状列表产生出来了。

    ◆第四步,运行表达式

    本功能的入口点在DCExpression.cs中的Eval()中,内部调用了根元素的Eval(),然后递归调用个子元素的Eval(),获得最终的运算结果。

    运算过程需要一个运行环境上下文对象,上下午对象只有2个功能:执行外界函数和取变量值。

    为此定义了IDCExpressionContext接口,其代码如下:

    namespace DCSoft.Expression
    {
        /// <summary>
        /// 表达式执行上下文对象
        /// </summary>
        [System.Runtime.InteropServices.ComVisible( false )]
        public interface IDCExpressionContext
        {
            /// <summary>
            /// 执行函数
            /// </summary>
            /// <param name="name">函数名</param>
            /// <param name="parameters">参数列表</param>
            /// <returns>函数返回值</returns>
            object ExecuteFunction(string name, object[] parameters);
            /// <summary>
            /// 获得变量值
            /// </summary>
            /// <param name="name">变量名</param>
            /// <returns>变量值</returns>
            object GetVariableValue(string name);
        }
    }

     

    应用程序只需定义一个实现了该接口的类型即可传递给表达式执行引擎即可进行数值表达式运算了。

     

    ◆◆应用

    这个表达式引擎完成后,开发者就能很方便的使用这个引擎了。首先是定义实现了IDCExpressionContext接口的上下文对象。其代码如下:

     

    private class MyContext : IDCExpressionContext
    {
        public object ExecuteFunction(string name, object[] parameters)
        {
            name = name.ToUpper();
            try
            {
                switch (name)
                {
                    case "SIN": return Math.Sin(Convert.ToDouble(parameters[0]));
                    case "MAX":
                        return Math.Max(
                                Convert.ToDouble(parameters[0]),
                                Convert.ToDouble(parameters[1]));
                    default:
                        MessageBox.Show("不支持的函数" + name);
                        return 0;
                }
            }
            catch (System.Exception ext)
            {
                MessageBox.Show("执行函数" + name + " 错误:" + ext.Message);
                return 0;
            }
        }
    
        public object GetVariableValue(string name)
        {
            name = name.ToUpper();
            switch( name )
            {
                case "A":return 1.5;
                case "B": return 2.3;
                case "C": return -99;
                case "D": return 998;
                case "E": return 123.5;
                case "F": return 3.1415;
                default:
                    MessageBox.Show("不支持的参数名:" + name);
                    return 0;
            }
        }
    }

     在这个上下文中,定义了函数SIN和MAX的执行过程,并定义了变量A,B,C,D,E,F的具体的数值

    然后我们可以使用以下代码来调用表达式引擎了。

    private void Form1_Load(object sender, EventArgs e)
    {
        cboExpression.Items.Add("SIN(99+123)");
        cboExpression.Items.Add("A*99.1+MAX(-11,-10)");
        cboExpression.Items.Add("FIND('7048dcb034d94ce6bfd750c3f1672096',[zz])>=0");
        cboExpression.Items.Add("SUM(A1:B3)");
        cboExpression.Items.Add("[A1]-[B2]");
        cboExpression.Items.Add("A+B+C+在三+是+213");
        cboExpression.Items.Add("A+B*(C+D*(E-F))-999");
        cboExpression.Items.Add("-A+B+C+D");
        cboExpression.Items.Add("A+B*C-D");
        cboExpression.Items.Add("10+8>-9");
    }
    private void toolStripButton1_Click(object sender, EventArgs e)
    {
        DCExpression exp = new DCExpression(cboExpression.Text);
        MyContext c = new MyContext();
        object vresult = exp.Eval(c);
        txtResult.Text = "运算结果:" + vresult;
    }

     

    如此这样我们用2500行代码实现了一个功能强大、性能卓越的数值表达式引擎。可广泛用于各类软件开发。

    在笔者主导开发的电子病历编辑器控件中就使用了这个数值表达式引擎来实现了类似EXCEL的公式运算功能,其界面如下:

     

    在这个文档中,我们设置右下角的单元格的数值表达式为"SUM([D3:D13])",则用户以下拉列表方式设置D3:D13区域的单元格内容时,软件会调用DCSoft.Expression的功能,自动执行数值运算,并将运算结果显示在右下角单元格中。

     

    ◆◆小结

    在本文中使用了2500行C#代码实现了一个结构清晰、易于理解和掌握、性能卓越的数值表达式运算引擎。支持四则数学运算、布尔逻辑运算、能调用外部函数和外部参数值。很容易实现一个类似EXCEL的公式运算引擎。

    使用该引擎,能显著提高软件的自由度,让软件在运行阶段即可自由改变运行模式,实现了软件的高度的运行时可配置化。

    中秋之夜编写此文,诗性大发:玉宇无尘,朗月虚空三千里;架构有道,代码奔腾百万行。献给红尘中这百万代码人共勉。

     

    ◆◆关于作者

    袁永福,80后,南京东南大学毕业,中国知名医疗信息技术专家,南京都昌信息科技有限公司(www.dcwriter.cn)的联合创始人,微软MVP,从事软件研发近20年。长期从事电子病历软件底层技术的研发和推广,对医学病历文档技术有着深厚的积累。

    转载于:https://www.cnblogs.com/xdesigner/p/9703613.html

    展开全文
  • C# 数值表达式计算器

    2019-03-03 17:16:10
    使用C#语言开发的计算器,可计算加减乘除,支持小数运算,自己编写,暂未发现bug,主要使用了栈,大家可以自己研究一下
  • 我编写了一个计算拉格朗日插值多项式数值表达式的函数:#!/usr/bin/env python#coding: utf8from sympy import *import jsondef polinomioLagrange(Xs, Ys, t):x = Symbol('x')expresion = ''for k in range(len(Xs)...
  • awk 系列:怎样使用 awk 变量、数值表达式以及赋值运算符 我觉得 awk 系列 将会越来越好,在本系列的前七节我们讨论了在 Linux 中处理文件和筛选字符串所需要的一些 awk 命令基础。 在这一部分,我们将会进入 awk 更...
  • 用于Python,NumPy,PyTables,pandas,bcolz等的快速数值数组表达式求值程序
  • 我觉得awk 系列将会越来越好,在本系列的前七节我们讨论了在 Linux 中处理文件和筛选字符串所...因此,我们将会讲到 Awk 的一些特性,诸如变量、数值表达式和赋值运算符。 学习 Awk 变量,数值表达式和赋值运算符 你...
  • 计算带括号的数值表达式

    千次阅读 2017-10-13 14:22:24
    输入一串数值算术表达式( 如:34+5-(8+4)*6 ) ,计算机编程实现对该表达式的树形存储(即:去除括号),并输出表达式的结构树和相应的前缀表达式和后缀表达式,以及完成求值运算。 算法: 共分为5个文件,4个头...
  • 数值表达式计算

    2012-06-02 05:32:53
     如何将中缀表达式转换成后缀表达式,方法如下(虽然给出,但建议搜百度):1、检测左括号。如无则退回原字符,如有则brankets入栈,opStack入栈。2、输入数字(如果使用了istringstream会很简单)。如成功则将之入...
  • 利用堆栈计算表达式的值,如:3×5-2+3×8/2实现+、-、×、/、(、)运算
  • 计算后缀表达式只用到一个栈,因此很多情况下需要将中缀表达式转换成后缀表达式,以方便进行计算。 使用条件:只能出现二元操作符加减乘除,不能出现负号等一元操作符 1. 如果中缀表达式以全括号形式表示,则可以...
  • 第2章 基本数据类型与数值表达式2.1 知识要点计算机的基本功能是进行数据处理。在C++语言中,数据处理的基本对象是常量和变量。运算是对各种形式的数据进行处理。数据在内存中存放的情况由数据类型所决定。数据的...
  • 是大学计算机的学栈的必须要做的经典题目,我饿可以求浮点数的做法
  • 里面是作者我做的同一个工程的几个不同版本。随着版本的递增,程序的功能等有了一定的加强。注:4.0版本单独上次了
  • 选中计算表达式,按ctrl+shift+=,不替换原来表达式,直接在后面添加等于号和结果 选中计算表达式,按ctrl+shift+c,直接用结果替换原有表达式 转载于:https://blog.51cto.com/maplebb/1930698
  • 用java解析字符串,如字符串"(1+2/5)*3"当成是数值表达式,进行计算出结果来import java.io.*;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;public class Main{public static void ...
  • 1.判断表达式括号是否配对 #include #include using namespace std; bool isBalanced(const string& expression) { const char left = '(', right = ')'; bool flag = true; stack chstack; char ch; for...
  • 本节书摘来自华章计算机《从问题到程序:用Python学编程和计算》一书中的第2章,第2.1节,作者 裘宗燕,更多章节内容可以访问云栖社区“华章计算机”公众号...人们在学数学时已经写过许多数学表达式,其中的一些表...
  • 本节书摘来自华章计算机《从问题到程序:用Python学编程和计算》一书中的第2章,第2.1节,作者:裘宗燕 更多章节内容可以访问云栖社区“华章计算机”公众号...人们在学数学时已经写过许多数学表达式,其中的一些表...
  • import java.io.*; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; public class Main ...public static void main(String[] args) throws Exception ...String expr = "(1+2/5)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,879
精华内容 4,751
关键字:

数值表达式