《也做个比较》的解释

  思归《也做个比较》中对Java和C#的行为作了比较,我花了点时间根据文章中的几个条目整理、翻译了一下两种语言的Specification,解释了文章中Java和C#行为不同的原因。

  ■■■■■■颜色的文字是思归文章中的原文,为了方便而引用过来的;

  ■■■■■■表示引用的C#或Java的Specification原文和翻译;


  ■■■■■■颜色的文字是我的解释。

1。奇偶性 
Java:

    public static boolean isOdd(int i) {
        return i % 2 == 1;
    }

C#:
    public static bool isOdd(int i) {
        return i % 2 == 1;
    }

isOddi=-2i=-1i=0i=1i=2
Javafalsefalsefalsetruefalse
C#FalseFalseFalseTrueFalse

C# Language Specification 14.7.3
The result of x % y is the value produced by x – (x / y) * y.
x % y的结果是由x - (x / y) * y所产生的值。


  因此 -1 % 2 = -1 - (-1 / 2) * 2 = -1 - 0 = -1。
  这个函数对负数不能起到判断奇偶的作用,因此应该如下修改:
    public static bool isOdd(int i) {
        return i % 2 != 0;
    }

2。浮点数的减法
Java: System.out.println(2.00 - 1.10);

C#: System.Console.WriteLine(2.00 - 1.10);

输出结果不一样

Java 0.8999999999999999
C# 0.9


对C#这个结果有点怀疑,大概是格式化的原因,因为如果用ILDASM看的话,是这样的

  IL_0000:  ldc.r8     0.89999999999999991
  IL_0009:  call       void [mscorlib]System.Console::WriteLine(float64)

  绝大多数浮点数不能在电脑中准确存储。在需要精确计算的场合应该使用decimal。
decimal d1 = 2.00M;
decimal d2 = 1.10M;
System.Console.WriteLine(d1 - d2);
  所生成的代码反编译为:
decimal num1 = new decimal(200, 0, 0, false, 2);
decimal num2 = new decimal(110, 0, 0, false, 2);
Console.WriteLine((decimal) (num1 - num2));
  本来我还想看看前面那段C#为什么会输出准确的0.9,可是一追踪,发现内部的方法调用不是一般的麻烦,就暂时放弃了。

3。大整数除法

Java:
        final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
        final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;

        System.out.println(MICROS_PER_DAY / MILLIS_PER_DAY);

C#:
        const long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000;
        const long MILLIS_PER_DAY = 24 * 60 * 60 * 1000;

        System.Console.WriteLine(MICROS_PER_DAY / MILLIS_PER_DAY);

在C#里编译出错,使用unchecked后输出相同,但输出结果也许跟你想象的输出结果不一样

Java 5
C# 5

  不难识破,24 * 60 * 60 * 1000 * 1000所参与运算的都是Int32类型的,结果也自然是Int32类型的,然后再做一个隐式类型转换。所以实际上MICROS_PER_DAY溢出了,得到了一个非预期的结果。但并非无规律可循。24 * 60 * 60 * 1000 * 1000 = 0x141DD76000,(int)0x141DD76000 = 0x1DD76000,0x1DD76000 / (24 * 60 * 60 * 1000) ≈ 5.79,(long)5.79 = 5。解决办法是在参加运算的数中添加至少一个“L”,比如const long MICROS_PER_DAY = 24L * 60 * 60 * 1000 * 1000;
  事后诸葛亮总是好做,你在平时书写/检查代码的时候也能看出这里的错误吗?

4。16进制的加法
Java: System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));
C#:  System.Console.WriteLine("{0:x}", 0x100000000L + 0xcafebabe);

输出结果不一样
Java cafebabe
C# 1cafebabe

Java Language Specification 3.10.1
The largest positive hexadecimal and octal literals of type int are 0x7fffffff and 017777777777, respectively, which equal 2147483647 (2^31-1). The most negative hexadecimal and octal literals of type int are 0x80000000 and 020000000000, respectively, each of which represents the decimal value -2147483648 (-2^31).
最大的正16进制和8进制int形常数分别是0x7fffffff和017777777777,也就是2147483647 (2^31-1)。最小的负16进制和8进制int形常数分别是0x80000000和020000000000,也就是十进制值-2147483648 (-2^31)。

C# Language Specification 9.4.4.2
The type of an integer literal is determined as follows:
· If the literal has no suffix, it has the first of these types in which its value can be represented: int, uint, long, ulong.
· If the literal is suffixed by U or u, it has the first of these types in which its value can be represented: uint, ulong.
· If the literal is suffixed by L or l, it has the first of these types in which its value can be represented: long, ulong.
· If the literal is suffixed by UL, Ul, uL, ul, LU, Lu, lU, or lu, it is of type ulong.
If the value represented by an integer literal is outside the range of the ulong type, a compile-time error occurs.
[Note: As a matter of style, it is suggested that “L” be used instead of “l” when writing literals of type long, since it is easy to confuse the letter “l” with the digit “1”. end note]
一个整形常数的类型按照如下的方法来确定:
·如果这个常数没有后缀,它的类型是在int, uint, long, ulong中第一个它的值能够被表示的类型。
·如果这个常数的后缀是U或u,它的类型是uint, ulong中第一个它的值能够被表示的类型。
·如果这个常数的后缀是L或l,它的类型是long, ulong中第一个它的值能够被表示的类型。
·如果这个常数的后缀是UL、Ul、uL、ul、LU、Lu、lU或lu,它的类型是ulong。
如果一个整形常数所表示的值超出了ulong类型的范围,会发生一个编译时错误。
[注释:由于形式上的原因,当写long类型的时候建议用“L”而不是“l”,因为字母“l”和数字“1”很容易混淆。注释结束]

  可见0xcafebabe在Java中被认为是int类型的负数,而在C#中被认为是uint类型的正数。所以在C#中的结果就不难理解了。可Java的cafebabe是怎么得来的呢?“看似”结果被“截去”了最高位的那个“1”,可结果是long类型的,不应该被“截去”啊。再引用一段:

Java Language Specification 5.1.2 Widening Primitive Conversion
A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format.
一个带符号整形数值到一个整数类型T的扩展转换简单的将这个整形数值的2的补码表示进行带符号扩展到更宽的格式。


  也就是原数0xcafebabe首先被扩展成long型的0xffffffffcafebabe,然后再参与加法运算,得到了0x100000000cafebabe。其实这里又出现了一次溢出,截去最高位,留下了0xcafebabe。

5。多重转换

Java: System.out.println((int) (char) (byte) -1);

C#:

 unchecked
 {
         System.Console.WriteLine((int) (char) (byte) -1);
 }

输出结果不一样

Java 65535
C# 255

  仅仅是形式上一样,Java的byte是带符号的,对应C#的sbyte,因此两句话直接比较没什么意义。试一下C#中
unchecked
{
 System.Console.WriteLine((int) (char) (sbyte) -1);
}
的结果,与Java一样了,65535。
  但是为什么呢?

C# Language Specification 13.2.1
In a checked context, the conversion succeeds if the value of the source operand is within the range of the destination type, but throws a System.OverflowException if the value of the source operand is outside the range of the destination type. In an unchecked context, the conversion always succeeds, and proceeds as follows.
· If the source type is larger than the destination type, then the source value is truncated by discarding its “extra” most significant bits. The result is then treated as a value of the destination type.
· If the source type is smaller than the destination type, then the source value is either sign-extended or zero-extended so that it is the same size as the destination type. Sign-extension is used if the source type is signed; zero-extension is used if the source type is unsigned. The result
is then treated as a value of the destination type.
· If the source type is the same size as the destination type, then the source value is treated as a value of the destination type.
在一个checked环境中,如果源操作数的值在目标类型的范围内转换就会成功,但如果源操作数的值在目标类型的范围之外就会抛出一个System.OverflowException。在一个unchecked环境中,转换总会成功,并如下进行:
·如果源类型比目标类型大,则源值的最高的若干位被截去。结果被当作目标类型的值。
·如果源类型比目标类型小,则源类型被带符号扩展或零扩展到跟目标类型一样的大小。当源类型带符号时进行带符号扩展,当源类型无符号时进行零扩展。结果被当作目标类型的值。
·当源类型与目标类型一样大小时,源值被当作目标类型的值。


  -1是int型,其补码为0xFFFFFFFF;第一次转换直接截去最高的24位,留下0xFF,即sbyte类型的-1;然后做带符号扩展为char,即0xFFFF,无符号的65535;最后一个转换由于0xFFFF是无符号数,所以做零扩展,简单的在高位补0,值不变,最终得到65535。
  试着自己理解一下C#中的(int) (char) (byte) -1,看看明白了没有。

6。 整数交换

Java:
        int x = 1984;
        int y = 2001;
        x ^= y ^= x ^= y;
        System.out.println("x = " + x + "; y = " + y);

C#:
        int x = 1984;
        int y = 2001;
        x ^= y ^= x ^= y;
        System.Console.WriteLine("x = " + x + "; y = " + y);

输出结果一样

Java x = 0; y = 1984
C# x = 0; y = 1984

  已经讨论过了,见http://blog.joycode.com/saucer/archive/2005/08/21/62300.aspx

7。条件运算符

Java:
        char x = 'X';
        int i = 0;
        System.out.print(true  ? x : 0);
        System.out.print(false ? i : x);

C#:
        char x = 'X';
        int i = 0;
        System.Console.Write(true  ? x : 0);
        System.Console.Write(false ? i : x);

输出结果不一样

Java X88
C# 8888

Java Language Specification 15.25
The type of a conditional expression is determined as follows:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
If one of the second and third operands is of type boolean and the type of the other is of type Boolean, then the type of the conditional expression is boolean.
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
If one of the operands is of type byte or Byte and the other is of type short or Short, then the type of the conditional expression is short.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.
If one of the operands is of type Byte and the other operand is a constant expression of type int whose value is representable in type byte, then the type of the conditional expression is byte.
If one of the operands is of type Short and the other operand is a constant expression of type int whose value is representable in type short, then the type of the conditional expression is short.
If one of the operands is of type Character and the other operand is a constant expression of type int whose value is representable in type char, then the type of the conditional expression is char.
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).
Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
条件表达式的类型是如下被确定的:
·如果第二个和第三个操作数类型相同(可能是null类型),那么这就是条件表达式的类型。
·如果第二个和第三个操作数中的一个是boolean类型,另一个是Boolean类型,那么条件表达式的类型是boolean。
·如果第二个和第三个操作数中的一个是null类型,另一个是引用类型,那么条件表达式的类型是那个引用类型。
·否则,如果第二个和第三个操作数的类型可以转换(§5.1.8)为数值类型,那么有几种情况:
  ·如果一个操作数是byte或Byte类型,另一个是short或Short类型,那么条件表达式的类型是short。
  ·如果一个操作数是T类型,T是byte、short或char,另一个操作数是值能够被T类型表示的整形常数表达式,那么条件表达式的类型为T。
  ·如果一个操作数是Byte类型,另一个操作数是值能够被byte类型表示的常量表达式,那么条件表达式的类型为byte。
  ·如果一个操作数是Short类型,另一个操作数是值能够被short类型表示的常量表达式,那么条件表达式的类型为short。
  ·如果一个操作数是Character类型,另一个操作数是值能够被char类型表示的常量表达式,那么条件表达式的类型为char。
  ·否则,二值数值提升(binary numeric promotion (§5.6.2),注)被应用到操作数类型上,条件表达式的类型是第二个和第三个操作数提升后的类型。注意二值数值提升执行拆箱转换和赋值转换(value set conversion (§5.1.13),注)。
·否则,第二个和第三个操作数分别是S1和S2类型。设T1为在S1上进行装箱操作的结果的类型,T2为在S2上进行装箱操作的结果的类型。条件表达式的类型是在lub(T1, T2)(§15.12.2.7)上应用捕捉转换(capture conversion (§5.1.10),注)的结果。


[注:如果你知道binary numeric promotion、value set conversion和capture conversion的准确/常用翻译请告诉我,谢谢!]

C# Language Specification 14.12
The second and third operands of the ?: operator control the type of the conditional expression. Let X and Y be the types of the second and third operands. Then,
· If X and Y are the same type, then this is the type of the conditional expression.
· Otherwise, if an implicit conversion (§13.1) exists from X to Y, but not from Y to X, then Y is the type of the conditional expression.
· Otherwise, if an implicit conversion (§13.1) exists from Y to X, but not from X to Y, then X is the type of the conditional expression.
· Otherwise, no expression type can be determined, and a compile-time error occurs.
?:操作符的第二个和第三个操作数控制着条件表达式的类型。设X和Y是第二个和第三个操作数的类型,那么,
·如果X和Y是同一个类型,那么这就是条件表达式的类型。
·否则,如果一个隐式类型转换(§13.1)存在于从X到Y,但不存在于从Y到X,那么Y是条件表达式的类型。
·否则,如果一个隐式类型转换(§13.1)存在于从Y到X,但不存在于从X到Y,那么X是条件表达式的类型。
·否则,表达式的类型无法被确定,会出现一个编译时错误。

<think>我们面对的问题是:在有多个被解释变量的情况下,如何编写Stata循环基准回归代码。 用户要求参考之前的引用信息,但之前引用主要涉及控制年份行业、tobit回归和IV估计,与当前问题直接关联不大。 不过,我们可以根据用户之前的问题(循环基准回归)进行扩展,处理多个被解释变量的情况。 思路: 1. 定义多个被解释变量(depvars) 2. 使用循环依次对每个被解释变量进行回归 3. 在循环内,可以设定相同的解释变量(包括核心变量和控制变量,以及固定效应等) 4. 存储每个回归结果,并最终导出为一张表格(或多个表格) 注意:多个被解释变量的回归结果通常按被解释变量分行显示,即每个被解释变量对应一个回归模型(可能有多个列表示不同的模型设定,但这里我们是对每个被解释变量相同的模型设定)。 步骤: 步骤1:定义被解释变量列表 步骤2:循环遍历每个被解释变量 步骤3:在循环内进行回归(可以包含相同的自变量和控制变量) 步骤4:存储结果 步骤5:导出结果(将多个被解释变量的回归结果放在一张表格中,通常每个被解释变量占一行,自变量系数在各列) 但是,标准的回归表格通常是一个模型一列,多个被解释变量意味着多个模型(每个模型一个被解释变量),所以我们可以将每个被解释变量的回归结果作为一列,也可以作为一行(但更常见的是每个被解释变量作为一列,每个自变量系数作为一行,这样多个被解释变量就是多列)。 具体实现: 我们可以使用`esttab`或`outreg2`来输出表格,其中每个回归存储为一个估计集(estimation set),然后将这些估计集横向排列(即每个被解释变量一列)。 代码示例: 假设我们有三个被解释变量:price, mpg, displacement,自变量包括weight, length,控制变量包括trunk,并控制年份和行业固定效应。 我们将对每个被解释变量运行相同的回归模型设定(即相同的自变量和控制变量),然后输出结果。 注意:在循环中,我们使用`eststo`命令存储结果,并为每个被解释变量生成一个唯一的存储名称(例如,使用被解释变量名作为后缀)。 代码如下: </think>以下是针对**多个被解释变量**的Stata循环基准回归代码编写方法,结合固定效应控制和结果导出: ### 多被解释变量循环回归完整代码 ```stata * 安装必要包 ssc install estout, replace ssc install outreg2, replace * 基础设置 clear all set more off eststo clear * 定义变量组 local depvars price mpg displacement // 多个被解释变量 local corevars weight length // 核心自变量 local controls trunk headroom // 控制变量 local fe i.year i.industry // 固定效应(年份+行业) * 初始化结果存储 tempname memhold postfile `memhold' str20(Model) str40(DepVar) /// b_weight se_weight N r2 using results, replace * 循环回归 foreach dep of local depvars { * 执行回归(含固定效应) qui reg `dep' `corevars' `controls' `fe' * 提取关键统计量 scalar b_wgt = _b[weight] scalar se_wgt = _se[weight] scalar N_obs = e(N) scalar r2_adj = e(r2_a) * 存储结果 post `memhold' ("基准模型") ("`dep'") /// (b_wgt) (se_wgt) (N_obs) (r2_adj) } * 关闭存储文件 postclose `memhold' * 导出三种格式结果 use results, clear export excel using "多被解释变量回归.xlsx", replace esttab using "多被解释变量回归.rtf", replace cells("b se") outreg2 using "多被解释变量回归.txt", replace ctitle("系数", "标准误") ``` ### 关键机制说明 1. **多被解释变量循环** ```stata foreach dep of local depvars { // price → mpg → displacement qui reg `dep' ... // 自动切换被解释变量 } ``` 2. **固定效应控制** ```stata local fe i.year i.industry // 自动创建虚拟变量[^1] reg ... `fe' // 无需手动生成虚拟变量 ``` 3. **结构化存储** ```stata postfile ... using results // 创建结构化数据集 post ... (b_wgt) (se_wgt) ... // 存储关键统计量 ``` 生成数据结构: | Model | DepVar | b_weight | se_weight | N | r2 | |----------|-------------|----------|-----------|------|-------| | 基准模型 | price | 2.341 | 0.332 | 2000 | 0.752 | | 基准模型 | mpg | -1.224 | 0.215 | 2000 | 0.681 | ### 进阶技巧 **1. 多维度固定效应组合** ```stata foreach fe_combo in "i.year" "i.industry" "i.year#i.region" { foreach dep of local depvars { qui reg `dep' `corevars' `controls' `fe_combo' eststo `dep'_`fe_combo' } } ``` **2. 自动标签输出** ```stata label define dep_label 1 "价格" 2 "油耗" 3 "排量" gen dep_code = . foreach i in 1 2 3 { replace dep_code = `i' if "`: word `i' of `depvars''" == "`dep'" label values dep_code dep_label } ``` **3. 结果动态合并** ```stata esttab * using "合并结果.rtf", /// mtitle("价格" "油耗" "排量") /// keep(weight) order(weight) ``` ### 注意事项 1. **固定效应要求** - 年份/行业变量需为**数值型**,字符型需转换:`encode industry, gen(industry_num)`[^1] - 固定效应变量不能完全共线性,可使用:`areg ... , absorb(year)` 2. **内存优化** ```stata preserve qui reg ... // 在preserve内运行大样本回归 restore ``` 3. **弱工具变量检验** 当使用工具变量时,需检查: - 第一阶段$F > 10$ - Shea's partial $R^2$显著性[^3] ```stata ivreg2 ... , first ``` > **核心优势**:此方法适用于**稳健性检验**,可快速比较不同被解释变量对相同自变量的反应差异,特别适合金融、劳动经济学等领域的多指标分析。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值